From 19fcec84d8d7d21e796c7624e521b60d28ee21ed Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:45:59 +0200 Subject: Adding upstream version 16.2.11+ds. Signed-off-by: Daniel Baumann --- src/jaegertracing/thrift/lib/erl/Makefile.am | 97 ++++ src/jaegertracing/thrift/lib/erl/README.md | 51 ++ .../thrift/lib/erl/coding_standards.md | 3 + .../thrift/lib/erl/include/thrift_constants.hrl | 62 ++ .../thrift/lib/erl/include/thrift_protocol.hrl | 66 +++ .../lib/erl/include/thrift_protocol_behaviour.hrl | 37 ++ .../lib/erl/include/thrift_transport_behaviour.hrl | 31 + src/jaegertracing/thrift/lib/erl/rebar.config | 3 + .../thrift/lib/erl/rebar.config.script | 7 + .../thrift/lib/erl/src/thrift.app.src | 74 +++ .../thrift/lib/erl/src/thrift_base64_transport.erl | 69 +++ .../thrift/lib/erl/src/thrift_binary_protocol.erl | 347 +++++++++++ .../lib/erl/src/thrift_buffered_transport.erl | 98 ++++ .../thrift/lib/erl/src/thrift_client.erl | 162 ++++++ .../thrift/lib/erl/src/thrift_client_util.erl | 112 ++++ .../thrift/lib/erl/src/thrift_compact_protocol.erl | 390 +++++++++++++ .../lib/erl/src/thrift_disk_log_transport.erl | 123 ++++ .../thrift/lib/erl/src/thrift_file_transport.erl | 115 ++++ .../thrift/lib/erl/src/thrift_framed_transport.erl | 126 ++++ .../thrift/lib/erl/src/thrift_http_transport.erl | 116 ++++ .../thrift/lib/erl/src/thrift_json_parser.erl | 419 ++++++++++++++ .../thrift/lib/erl/src/thrift_json_protocol.erl | 567 ++++++++++++++++++ .../lib/erl/src/thrift_membuffer_transport.erl | 83 +++ .../thrift/lib/erl/src/thrift_memory_buffer.erl | 47 ++ .../lib/erl/src/thrift_multiplexed_map_wrapper.erl | 57 ++ .../lib/erl/src/thrift_multiplexed_protocol.erl | 83 +++ .../thrift/lib/erl/src/thrift_processor.erl | 219 +++++++ .../thrift/lib/erl/src/thrift_protocol.erl | 412 +++++++++++++ .../lib/erl/src/thrift_reconnecting_client.erl | 258 +++++++++ .../thrift/lib/erl/src/thrift_server.erl | 183 ++++++ .../thrift/lib/erl/src/thrift_service.erl | 25 + .../thrift/lib/erl/src/thrift_socket_server.erl | 324 +++++++++++ .../thrift/lib/erl/src/thrift_socket_transport.erl | 176 ++++++ .../lib/erl/src/thrift_sslsocket_transport.erl | 147 +++++ .../thrift/lib/erl/src/thrift_transport.erl | 126 ++++ .../lib/erl/src/thrift_transport_state_test.erl | 117 ++++ .../thrift/lib/erl/test/Thrift1151.thrift | 3 + .../thrift/lib/erl/test/Thrift1475.thrift | 34 ++ .../thrift/lib/erl/test/Thrift_omit_with.thrift | 22 + .../thrift/lib/erl/test/flags/LegacyNames.thrift | 33 ++ .../thrift/lib/erl/test/flags/Thrift3214.thrift | 23 + .../thrift/lib/erl/test/legacy_names_test.erl | 69 +++ .../thrift/lib/erl/test/multiplexing.thrift | 7 + .../thrift/lib/erl/test/multiplexing_test.erl | 57 ++ .../thrift/lib/erl/test/name_conflict_test.erl | 299 ++++++++++ .../thrift/lib/erl/test/stress_server.erl | 64 ++ .../thrift/lib/erl/test/test_const.erl | 54 ++ .../thrift/lib/erl/test/test_disklog.erl | 99 ++++ .../thrift/lib/erl/test/test_omit.erl | 79 +++ .../erl/test/test_rendered_double_constants.erl | 68 +++ .../thrift/lib/erl/test/test_thrift_1151.erl | 34 ++ .../thrift/lib/erl/test/test_thrift_3214.erl | 60 ++ .../erl/test/test_thrift_buffered_transport.erl | 359 ++++++++++++ .../lib/erl/test/test_thrift_compact_protocol.erl | 219 +++++++ .../lib/erl/test/test_thrift_file_transport.erl | 213 +++++++ .../lib/erl/test/test_thrift_framed_transport.erl | 404 +++++++++++++ .../erl/test/test_thrift_membuffer_transport.erl | 167 ++++++ .../lib/erl/test/test_thrift_socket_transport.erl | 199 +++++++ .../lib/erl/test/thrift_socket_server_test.erl | 49 ++ .../thrift/lib/erl/test/thrift_test_test.erl | 643 +++++++++++++++++++++ 60 files changed, 8590 insertions(+) create mode 100644 src/jaegertracing/thrift/lib/erl/Makefile.am create mode 100644 src/jaegertracing/thrift/lib/erl/README.md create mode 100644 src/jaegertracing/thrift/lib/erl/coding_standards.md create mode 100644 src/jaegertracing/thrift/lib/erl/include/thrift_constants.hrl create mode 100644 src/jaegertracing/thrift/lib/erl/include/thrift_protocol.hrl create mode 100644 src/jaegertracing/thrift/lib/erl/include/thrift_protocol_behaviour.hrl create mode 100644 src/jaegertracing/thrift/lib/erl/include/thrift_transport_behaviour.hrl create mode 100644 src/jaegertracing/thrift/lib/erl/rebar.config create mode 100644 src/jaegertracing/thrift/lib/erl/rebar.config.script create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift.app.src create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_base64_transport.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_binary_protocol.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_buffered_transport.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_client.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_client_util.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_compact_protocol.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_disk_log_transport.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_file_transport.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_framed_transport.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_http_transport.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_json_parser.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_json_protocol.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_membuffer_transport.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_memory_buffer.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_multiplexed_map_wrapper.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_multiplexed_protocol.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_processor.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_protocol.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_reconnecting_client.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_server.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_service.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_socket_server.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_socket_transport.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_sslsocket_transport.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_transport.erl create mode 100644 src/jaegertracing/thrift/lib/erl/src/thrift_transport_state_test.erl create mode 100644 src/jaegertracing/thrift/lib/erl/test/Thrift1151.thrift create mode 100644 src/jaegertracing/thrift/lib/erl/test/Thrift1475.thrift create mode 100644 src/jaegertracing/thrift/lib/erl/test/Thrift_omit_with.thrift create mode 100644 src/jaegertracing/thrift/lib/erl/test/flags/LegacyNames.thrift create mode 100644 src/jaegertracing/thrift/lib/erl/test/flags/Thrift3214.thrift create mode 100644 src/jaegertracing/thrift/lib/erl/test/legacy_names_test.erl create mode 100644 src/jaegertracing/thrift/lib/erl/test/multiplexing.thrift create mode 100644 src/jaegertracing/thrift/lib/erl/test/multiplexing_test.erl create mode 100644 src/jaegertracing/thrift/lib/erl/test/name_conflict_test.erl create mode 100644 src/jaegertracing/thrift/lib/erl/test/stress_server.erl create mode 100644 src/jaegertracing/thrift/lib/erl/test/test_const.erl create mode 100644 src/jaegertracing/thrift/lib/erl/test/test_disklog.erl create mode 100644 src/jaegertracing/thrift/lib/erl/test/test_omit.erl create mode 100644 src/jaegertracing/thrift/lib/erl/test/test_rendered_double_constants.erl create mode 100644 src/jaegertracing/thrift/lib/erl/test/test_thrift_1151.erl create mode 100644 src/jaegertracing/thrift/lib/erl/test/test_thrift_3214.erl create mode 100644 src/jaegertracing/thrift/lib/erl/test/test_thrift_buffered_transport.erl create mode 100644 src/jaegertracing/thrift/lib/erl/test/test_thrift_compact_protocol.erl create mode 100644 src/jaegertracing/thrift/lib/erl/test/test_thrift_file_transport.erl create mode 100644 src/jaegertracing/thrift/lib/erl/test/test_thrift_framed_transport.erl create mode 100644 src/jaegertracing/thrift/lib/erl/test/test_thrift_membuffer_transport.erl create mode 100644 src/jaegertracing/thrift/lib/erl/test/test_thrift_socket_transport.erl create mode 100644 src/jaegertracing/thrift/lib/erl/test/thrift_socket_server_test.erl create mode 100644 src/jaegertracing/thrift/lib/erl/test/thrift_test_test.erl (limited to 'src/jaegertracing/thrift/lib/erl') diff --git a/src/jaegertracing/thrift/lib/erl/Makefile.am b/src/jaegertracing/thrift/lib/erl/Makefile.am new file mode 100644 index 000000000..d4544a339 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/Makefile.am @@ -0,0 +1,97 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +THRIFT = ../../compiler/cpp/thrift +THRIFT_OMIT_FILE = test/Thrift_omit_without.thrift +THRIFT_FILES = $(wildcard test/*.thrift) \ + $(THRIFT_OMIT_FILE) \ + ../../test/ConstantsDemo.thrift \ + ../../test/NameConflictTest.thrift \ + ../../test/DoubleConstantsTest.thrift \ + ../../test/ThriftTest.thrift + +if ERLANG_OTP16 +ERL_FLAG = erl:otp16 +ERL_FLAG_LEGACY = erl:otp16,legacynames +# otp16 + maps does not make sense. We need to generate it anyway to avoid include error. +ERL_FLAG_MAPS = erl:otp16 +else +ERL_FLAG = erl +ERL_FLAG_LEGACY = erl:legacynames +ERL_FLAG_MAPS = erl:maps +endif + +$(THRIFT_OMIT_FILE): test/Thrift_omit_with.thrift + grep -v omit $< >$@ + +.generated: $(THRIFT) $(THRIFT_FILES) + for f in $(THRIFT_FILES) ; do \ + $(THRIFT) --gen $(ERL_FLAG) -o test $$f ; \ + done + $(THRIFT) --gen $(ERL_FLAG_LEGACY) -o test test/flags/LegacyNames.thrift + $(THRIFT) --gen $(ERL_FLAG_MAPS) -o test test/flags/Thrift3214.thrift + touch .generated + +all: .generated + $(REBAR) compile + +check: .generated + $(REBAR) eunit + +install: all + mkdir -p $(DESTDIR)$(ERLANG_INSTALL_LIB_DIR_thrift) ; \ + mkdir -p $(DESTDIR)$(ERLANG_INSTALL_LIB_DIR_thrift)/ebin ; \ + mkdir -p $(DESTDIR)$(ERLANG_INSTALL_LIB_DIR_thrift)/include ; \ + mkdir -p $(DESTDIR)$(ERLANG_INSTALL_LIB_DIR_thrift)/src ; \ + for p in ebin/*.app* ebin/*.beam include/*.hrl src/*.erl ; \ + do $(INSTALL) $$p $(DESTDIR)$(ERLANG_INSTALL_LIB_DIR_thrift)/$$p ; \ + done + +uninstall: + $(RM) -rf $(DESTDIR)$(ERLANG_INSTALL_LIB_DIR_thrift) + +clean-local: + $(REBAR) clean + $(RM) .generated + $(RM) -r .rebar/ + $(RM) -r _build/ + $(RM) -r test/gen-erl/ + $(RM) $(THRIFT_OMIT_FILE) + +maintainer-clean-local: + $(RM) -r ebin/ + +dist-hook: + $(RM) $(distdir)/.generated + $(RM) -r $(distdir)/.rebar/ + $(RM) -r $(distdir)/_build/ + $(RM) -r $(distdir)/ebin/ + $(RM) -r $(distdir)/test/gen-erl/ + $(RM) $(distdir)/$(THRIFT_OMIT_FILE) + +EXTRA_DIST = \ + include \ + src \ + coding_standards.md \ + rebar.config \ + rebar.config.script \ + test \ + README.md + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/jaegertracing/thrift/lib/erl/README.md b/src/jaegertracing/thrift/lib/erl/README.md new file mode 100644 index 000000000..433d05c34 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/README.md @@ -0,0 +1,51 @@ +# Thrift Erlang Software Library # + +## License ## + +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. + +## Release Notes ## + +### 0.9.2 ### + +as of 0.9.2 struct and function naming conventions have changed. to retain the +old naming conventions (for backwards compatibility) use the compiler option +`legacynames` + +## Example ## + +Example session using thrift_client: + +```erl +1> {ok, C0} = thrift_client_util:new("localhost", 9090, thrift_test_thrift, []), ok. +ok +2> {C1, R1} = thrift_client:call(C0, testVoid, []), R1. +{ok,ok} +3> {C2, R2} = thrift_client:call(C1, testVoid, [asdf]), R2. +{error,{bad_args,testVoid,[asdf]}} +4> {C3, R3} = thrift_client:call(C2, testI32, [123]), R3. +{ok,123} +5> {C4, R4} = thrift_client:call(C3, testOneway, [1]), R4. +{ok,ok} +6> {C5, R5} = thrift_client:call(C4, testXception, ["foo"]), R5. +{error,{no_function,testXception}} +7> {C6, R6} = thrift_client:call(C5, testException, ["foo"]), R6. +{ok,ok} +8> {C7, R7} = (catch thrift_client:call(C6, testException, ["Xception"])), R7. +{exception,{xception,1001,<<"Xception">>}} +``` diff --git a/src/jaegertracing/thrift/lib/erl/coding_standards.md b/src/jaegertracing/thrift/lib/erl/coding_standards.md new file mode 100644 index 000000000..4d37dbcc6 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/coding_standards.md @@ -0,0 +1,3 @@ +Please follow [General Coding Standards](/doc/coding_standards.md) + +Particularly for Erlang please follow the Erlang [Programming Rules and Conventions](http://www.erlang.se/doc/programming_rules.shtml). diff --git a/src/jaegertracing/thrift/lib/erl/include/thrift_constants.hrl b/src/jaegertracing/thrift/lib/erl/include/thrift_constants.hrl new file mode 100644 index 000000000..7cb29ebdb --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/include/thrift_constants.hrl @@ -0,0 +1,62 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +%% TType +-define(tType_STOP, 0). +-define(tType_VOID, 1). +-define(tType_BOOL, 2). +-define(tType_BYTE, 3). +-define(tType_I8, 3). +-define(tType_DOUBLE, 4). +-define(tType_I16, 6). +-define(tType_I32, 8). +-define(tType_I64, 10). +-define(tType_STRING, 11). +-define(tType_STRUCT, 12). +-define(tType_MAP, 13). +-define(tType_SET, 14). +-define(tType_LIST, 15). + +% TMessageType +-define(tMessageType_CALL, 1). +-define(tMessageType_REPLY, 2). +-define(tMessageType_EXCEPTION, 3). +-define(tMessageType_ONEWAY, 4). + +% TApplicationException +-define(TApplicationException_Structure, + {struct, [{1, string}, + {2, i32}]}). + +-record('TApplicationException', {message, type}). + +-define(TApplicationException_UNKNOWN, 0). +-define(TApplicationException_UNKNOWN_METHOD, 1). +-define(TApplicationException_INVALID_MESSAGE_TYPE, 2). +-define(TApplicationException_WRONG_METHOD_NAME, 3). +-define(TApplicationException_BAD_SEQUENCE_ID, 4). +-define(TApplicationException_MISSING_RESULT, 5). +-define(TApplicationException_INTERNAL_ERROR, 6). +-define(TApplicationException_PROTOCOL_ERROR, 7). +-define(TApplicationException_INVALID_TRANSFORM, 8). +-define(TApplicationException_INVALID_PROTOCOL, 9). +-define(TApplicationException_UNSUPPORTED_CLIENT_TYPE, 10). + +-define (MULTIPLEXED_SERVICE_SEPARATOR, ":"). +-define (MULTIPLEXED_ERROR_HANDLER_KEY, "error_handler"). diff --git a/src/jaegertracing/thrift/lib/erl/include/thrift_protocol.hrl b/src/jaegertracing/thrift/lib/erl/include/thrift_protocol.hrl new file mode 100644 index 000000000..bc0acc883 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/include/thrift_protocol.hrl @@ -0,0 +1,66 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-ifndef(THRIFT_PROTOCOL_INCLUDED). +-define(THRIFT_PROTOCOL_INCLUDED, true). + +-record(protocol_message_begin, {name :: string(), type :: integer(), seqid :: integer()}). +-record(protocol_struct_begin, {name :: string()}). +-record(protocol_field_begin, {name :: string(), type :: integer(), id :: integer()}). +-record(protocol_map_begin, {ktype :: integer(), vtype :: integer(), size :: integer()}). +-record(protocol_list_begin, {etype :: integer(), size :: integer()}). +-record(protocol_set_begin, {etype :: integer(), size :: integer()}). + +-type tprot_header_val() :: #protocol_message_begin{} + | #protocol_struct_begin{} + | #protocol_field_begin{} + | #protocol_map_begin{} + | #protocol_list_begin{} + | #protocol_set_begin{} + . +-type tprot_empty_tag() :: message_end + | struct_begin + | struct_end + | field_end + | map_end + | list_end + | set_end + . +-type tprot_header_tag() :: message_begin + | field_begin + | map_begin + | list_begin + | set_begin + . +-type tprot_data_tag() :: ui32 + | bool + | byte + | i16 + | i32 + | i64 + | double + | string + . +-type tprot_cont_tag() :: {list, _Type} + | {map, _KType, _VType} + | {set, _Type} + . + + +-endif. diff --git a/src/jaegertracing/thrift/lib/erl/include/thrift_protocol_behaviour.hrl b/src/jaegertracing/thrift/lib/erl/include/thrift_protocol_behaviour.hrl new file mode 100644 index 000000000..abe300b1b --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/include/thrift_protocol_behaviour.hrl @@ -0,0 +1,37 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +%% Signature specifications for protocol implementations. + +-ifndef(THRIFT_PROTOCOL_BEHAVIOUR_INCLUDED). +-define(THRIFT_PROTOCOL_BEHAVIOUR_INCLUDED, true). + +-spec flush_transport(state()) -> {state(), ok | {error, _Reason}}. +-spec close_transport(state()) -> {state(), ok | {error, _Reason}}. + +-spec write(state(), any()) -> {state(), ok | {error, _Reason}}. + +%% NOTE: Keep this in sync with thrift_protocol:read and read_specific. +-spec read + (state(), tprot_empty_tag()) -> {state(), ok | {error, _Reason}}; + (state(), tprot_header_tag()) -> {state(), tprot_header_val() | {error, _Reason}}; + (state(), tprot_data_tag()) -> {state(), {ok, any()} | {error, _Reason}}. + + +-endif. diff --git a/src/jaegertracing/thrift/lib/erl/include/thrift_transport_behaviour.hrl b/src/jaegertracing/thrift/lib/erl/include/thrift_transport_behaviour.hrl new file mode 100644 index 000000000..dbc05aacf --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/include/thrift_transport_behaviour.hrl @@ -0,0 +1,31 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +%% Signature specifications for transport implementations. + +-ifndef(THRIFT_TRANSPORT_BEHAVIOUR_INCLUDED). +-define(THRIFT_TRANSPORT_BEHAVIOUR_INCLUDED, true). + +-spec write(state(), iolist() | binary()) -> {state(), ok | {error, _Reason}}. +-spec read(state(), non_neg_integer()) -> {state(), {ok, binary()} | {error, _Reason}}. +-spec flush(state()) -> {state(), ok | {error, _Reason}}. +-spec close(state()) -> {state(), ok | {error, _Reason}}. + + +-endif. diff --git a/src/jaegertracing/thrift/lib/erl/rebar.config b/src/jaegertracing/thrift/lib/erl/rebar.config new file mode 100644 index 000000000..1b32947a5 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/rebar.config @@ -0,0 +1,3 @@ +{erl_opts, [{platform_define, "^R.*", otp16_or_less}, debug_info]}. + +{profiles, [{test, [{deps, [meck]}]}]}. diff --git a/src/jaegertracing/thrift/lib/erl/rebar.config.script b/src/jaegertracing/thrift/lib/erl/rebar.config.script new file mode 100644 index 000000000..c73382326 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/rebar.config.script @@ -0,0 +1,7 @@ +Def0 = case not erlang:is_builtin(erlang, monotonic_time, 0) of + true -> []; + false -> [{d, time_correction}] + end, +Defs = Def0, +lists:keystore(erl_opts, 1, CONFIG, + {erl_opts, proplists:get_value(erl_opts, CONFIG, []) ++ Defs}). diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift.app.src b/src/jaegertracing/thrift/lib/erl/src/thrift.app.src new file mode 100644 index 000000000..e5e6ed5cf --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift.app.src @@ -0,0 +1,74 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%%% -*- mode:erlang -*- +{application, thrift, [ + % A quick description of the application. + {description, "Thrift bindings"}, + + % The version of the applicaton + {vsn, "0.13.0"}, + + % All modules used by the application. + {modules, [ + thrift_base64_transport, + thrift_binary_protocol, + thrift_buffered_transport, + thrift_client_util, + thrift_client, + thrift_disk_log_transport, + thrift_file_transport, + thrift_framed_transport, + thrift_http_transport, + thrift_json_parser, + thrift_json_protocol, + thrift_membuffer_transport, + thrift_memory_buffer, + thrift_processor, + thrift_protocol, + thrift_reconnecting_client, + thrift_server, + thrift_service, + thrift_socket_server, + thrift_socket_transport, + thrift_transport_state_test, + thrift_transport + ]}, + + % All of the registered names the application uses. This can be ignored. + {registered, []}, + + % Applications that are to be started prior to this one. This can be ignored + % leave it alone unless you understand it well and let the .rel files in + % your release handle this. + {applications, [kernel, stdlib]}, + + % OTP application loader will load, but not start, included apps. Again + % this can be ignored as well. To load but not start an application it + % is easier to include it in the .rel file followed by the atom 'none' + {included_applications, []}, + + % configuration parameters similar to those in the config file specified + % on the command line. can be fetched with gas:get_env + {env, [ + % If an error/crash occurs during processing of a function, + % should the TApplicationException serialized back to the client + % include the erlang backtrace? + {exceptions_include_traces, true} + ]} +]}. diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_base64_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_base64_transport.erl new file mode 100644 index 000000000..d31f2bacf --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_base64_transport.erl @@ -0,0 +1,69 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_base64_transport). + +-behaviour(thrift_transport). + +%% API +-export([new/1, new_transport_factory/1]). + +%% thrift_transport callbacks +-export([write/2, read/2, flush/1, close/1]). + +%% State +-record(b64_transport, {wrapped}). +-type state() :: #b64_transport{}. +-include("thrift_transport_behaviour.hrl"). + +new(Wrapped) -> + State = #b64_transport{wrapped = Wrapped}, + thrift_transport:new(?MODULE, State). + + +write(This = #b64_transport{wrapped = Wrapped}, Data) -> + {NewWrapped, Result} = thrift_transport:write(Wrapped, base64:encode(iolist_to_binary(Data))), + {This#b64_transport{wrapped = NewWrapped}, Result}. + + +%% base64 doesn't support reading quite yet since it would involve +%% nasty buffering and such +read(This = #b64_transport{}, _Data) -> + {This, {error, no_reads_allowed}}. + + +flush(This = #b64_transport{wrapped = Wrapped0}) -> + {Wrapped1, ok} = thrift_transport:write(Wrapped0, <<"\n">>), + {Wrapped2, ok} = thrift_transport:flush(Wrapped1), + {This#b64_transport{wrapped = Wrapped2}, ok}. + + +close(This0) -> + {This1 = #b64_transport{wrapped = Wrapped}, ok} = flush(This0), + {NewWrapped, ok} = thrift_transport:close(Wrapped), + {This1#b64_transport{wrapped = NewWrapped}, ok}. + + +%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +new_transport_factory(WrapFactory) -> + F = fun() -> + {ok, Wrapped} = WrapFactory(), + new(Wrapped) + end, + {ok, F}. diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_binary_protocol.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_binary_protocol.erl new file mode 100644 index 000000000..85abb62d2 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_binary_protocol.erl @@ -0,0 +1,347 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_binary_protocol). + +-behaviour(thrift_protocol). + +-include("thrift_constants.hrl"). +-include("thrift_protocol.hrl"). + +-export([new/1, new/2, + read/2, + write/2, + flush_transport/1, + close_transport/1, + + new_protocol_factory/2 + ]). + +-record(binary_protocol, {transport, + strict_read=true, + strict_write=true + }). +-type state() :: #binary_protocol{}. +-include("thrift_protocol_behaviour.hrl"). + +-define(VERSION_MASK, 16#FFFF0000). +-define(VERSION_1, 16#80010000). +-define(TYPE_MASK, 16#000000ff). + +new(Transport) -> + new(Transport, _Options = []). + +new(Transport, Options) -> + State = #binary_protocol{transport = Transport}, + State1 = parse_options(Options, State), + thrift_protocol:new(?MODULE, State1). + +parse_options([], State) -> + State; +parse_options([{strict_read, Bool} | Rest], State) when is_boolean(Bool) -> + parse_options(Rest, State#binary_protocol{strict_read=Bool}); +parse_options([{strict_write, Bool} | Rest], State) when is_boolean(Bool) -> + parse_options(Rest, State#binary_protocol{strict_write=Bool}). + + +flush_transport(This = #binary_protocol{transport = Transport}) -> + {NewTransport, Result} = thrift_transport:flush(Transport), + {This#binary_protocol{transport = NewTransport}, Result}. + +close_transport(This = #binary_protocol{transport = Transport}) -> + {NewTransport, Result} = thrift_transport:close(Transport), + {This#binary_protocol{transport = NewTransport}, Result}. + +%%% +%%% instance methods +%%% + +write(This0, #protocol_message_begin{ + name = Name, + type = Type, + seqid = Seqid}) -> + case This0#binary_protocol.strict_write of + true -> + {This1, ok} = write(This0, {i32, ?VERSION_1 bor Type}), + {This2, ok} = write(This1, {string, Name}), + {This3, ok} = write(This2, {i32, Seqid}), + {This3, ok}; + false -> + {This1, ok} = write(This0, {string, Name}), + {This2, ok} = write(This1, {byte, Type}), + {This3, ok} = write(This2, {i32, Seqid}), + {This3, ok} + end; + +write(This, message_end) -> {This, ok}; + +write(This0, #protocol_field_begin{ + name = _Name, + type = Type, + id = Id}) -> + {This1, ok} = write(This0, {byte, Type}), + {This2, ok} = write(This1, {i16, Id}), + {This2, ok}; + +write(This, field_stop) -> + write(This, {byte, ?tType_STOP}); + +write(This, field_end) -> {This, ok}; + +write(This0, #protocol_map_begin{ + ktype = Ktype, + vtype = Vtype, + size = Size}) -> + {This1, ok} = write(This0, {byte, Ktype}), + {This2, ok} = write(This1, {byte, Vtype}), + {This3, ok} = write(This2, {i32, Size}), + {This3, ok}; + +write(This, map_end) -> {This, ok}; + +write(This0, #protocol_list_begin{ + etype = Etype, + size = Size}) -> + {This1, ok} = write(This0, {byte, Etype}), + {This2, ok} = write(This1, {i32, Size}), + {This2, ok}; + +write(This, list_end) -> {This, ok}; + +write(This0, #protocol_set_begin{ + etype = Etype, + size = Size}) -> + {This1, ok} = write(This0, {byte, Etype}), + {This2, ok} = write(This1, {i32, Size}), + {This2, ok}; + +write(This, set_end) -> {This, ok}; + +write(This, #protocol_struct_begin{}) -> {This, ok}; +write(This, struct_end) -> {This, ok}; + +write(This, {bool, true}) -> write(This, {byte, 1}); +write(This, {bool, false}) -> write(This, {byte, 0}); + +write(This, {byte, Byte}) -> + write(This, <>); + +write(This, {i16, I16}) -> + write(This, <>); + +write(This, {i32, I32}) -> + write(This, <>); + +write(This, {i64, I64}) -> + write(This, <>); + +write(This, {double, Double}) -> + write(This, <>); + +write(This0, {string, Str}) when is_list(Str) -> + {This1, ok} = write(This0, {i32, length(Str)}), + {This2, ok} = write(This1, list_to_binary(Str)), + {This2, ok}; + +write(This0, {string, Bin}) when is_binary(Bin) -> + {This1, ok} = write(This0, {i32, size(Bin)}), + {This2, ok} = write(This1, Bin), + {This2, ok}; + +%% Data :: iolist() +write(This = #binary_protocol{transport = Trans}, Data) -> + {NewTransport, Result} = thrift_transport:write(Trans, Data), + {This#binary_protocol{transport = NewTransport}, Result}. + +%% + +read(This0, message_begin) -> + {This1, Initial} = read(This0, ui32), + case Initial of + {ok, Sz} when Sz band ?VERSION_MASK =:= ?VERSION_1 -> + %% we're at version 1 + {This2, {ok, Name}} = read(This1, string), + {This3, {ok, SeqId}} = read(This2, i32), + Type = Sz band ?TYPE_MASK, + {This3, #protocol_message_begin{name = binary_to_list(Name), + type = Type, + seqid = SeqId}}; + + {ok, Sz} when Sz < 0 -> + %% there's a version number but it's unexpected + {This1, {error, {bad_binary_protocol_version, Sz}}}; + + {ok, _Sz} when This1#binary_protocol.strict_read =:= true -> + %% strict_read is true and there's no version header; that's an error + {This1, {error, no_binary_protocol_version}}; + + {ok, Sz} when This1#binary_protocol.strict_read =:= false -> + %% strict_read is false, so just read the old way + {This2, {ok, Name}} = read_data(This1, Sz), + {This3, {ok, Type}} = read(This2, byte), + {This4, {ok, SeqId}} = read(This3, i32), + {This4, #protocol_message_begin{name = binary_to_list(Name), + type = Type, + seqid = SeqId}}; + + Else -> + {This1, Else} + end; + +read(This, message_end) -> {This, ok}; + +read(This, struct_begin) -> {This, ok}; +read(This, struct_end) -> {This, ok}; + +read(This0, field_begin) -> + {This1, Result} = read(This0, byte), + case Result of + {ok, Type = ?tType_STOP} -> + {This1, #protocol_field_begin{type = Type}}; + {ok, Type} -> + {This2, {ok, Id}} = read(This1, i16), + {This2, #protocol_field_begin{type = Type, + id = Id}} + end; + +read(This, field_end) -> {This, ok}; + +read(This0, map_begin) -> + {This1, {ok, Ktype}} = read(This0, byte), + {This2, {ok, Vtype}} = read(This1, byte), + {This3, {ok, Size}} = read(This2, i32), + {This3, #protocol_map_begin{ktype = Ktype, + vtype = Vtype, + size = Size}}; +read(This, map_end) -> {This, ok}; + +read(This0, list_begin) -> + {This1, {ok, Etype}} = read(This0, byte), + {This2, {ok, Size}} = read(This1, i32), + {This2, #protocol_list_begin{etype = Etype, + size = Size}}; +read(This, list_end) -> {This, ok}; + +read(This0, set_begin) -> + {This1, {ok, Etype}} = read(This0, byte), + {This2, {ok, Size}} = read(This1, i32), + {This2, #protocol_set_begin{etype = Etype, + size = Size}}; +read(This, set_end) -> {This, ok}; + +read(This0, field_stop) -> + {This1, {ok, ?tType_STOP}} = read(This0, byte), + {This1, ok}; + +%% + +read(This0, bool) -> + {This1, Result} = read(This0, byte), + case Result of + {ok, Byte} -> {This1, {ok, Byte /= 0}}; + Else -> {This1, Else} + end; + +read(This0, byte) -> + {This1, Bytes} = read_data(This0, 1), + case Bytes of + {ok, <>} -> {This1, {ok, Val}}; + Else -> {This1, Else} + end; + +read(This0, i16) -> + {This1, Bytes} = read_data(This0, 2), + case Bytes of + {ok, <>} -> {This1, {ok, Val}}; + Else -> {This1, Else} + end; + +read(This0, i32) -> + {This1, Bytes} = read_data(This0, 4), + case Bytes of + {ok, <>} -> {This1, {ok, Val}}; + Else -> {This1, Else} + end; + +%% unsigned ints aren't used by thrift itself, but it's used for the parsing +%% of the packet version header. Without this special function BEAM works fine +%% but hipe thinks it received a bad version header. +read(This0, ui32) -> + {This1, Bytes} = read_data(This0, 4), + case Bytes of + {ok, <>} -> {This1, {ok, Val}}; + Else -> {This1, Else} + end; + +read(This0, i64) -> + {This1, Bytes} = read_data(This0, 8), + case Bytes of + {ok, <>} -> {This1, {ok, Val}}; + Else -> {This1, Else} + end; + +read(This0, double) -> + {This1, Bytes} = read_data(This0, 8), + case Bytes of + {ok, <>} -> {This1, {ok, Val}}; + Else -> {This1, Else} + end; + +% returns a binary directly, call binary_to_list if necessary +read(This0, string) -> + {This1, {ok, Sz}} = read(This0, i32), + read_data(This1, Sz). + +-spec read_data(#binary_protocol{}, non_neg_integer()) -> + {#binary_protocol{}, {ok, binary()} | {error, _Reason}}. +read_data(This, 0) -> {This, {ok, <<>>}}; +read_data(This = #binary_protocol{transport = Trans}, Len) when is_integer(Len) andalso Len > 0 -> + {NewTransport, Result} = thrift_transport:read(Trans, Len), + {This#binary_protocol{transport = NewTransport}, Result}. + + +%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-record(tbp_opts, {strict_read = true, + strict_write = true}). + +parse_factory_options([], Opts) -> + Opts; +parse_factory_options([{strict_read, Bool} | Rest], Opts) when is_boolean(Bool) -> + parse_factory_options(Rest, Opts#tbp_opts{strict_read=Bool}); +parse_factory_options([{strict_write, Bool} | Rest], Opts) when is_boolean(Bool) -> + parse_factory_options(Rest, Opts#tbp_opts{strict_write=Bool}). + + +%% returns a (fun() -> thrift_protocol()) +new_protocol_factory(TransportFactory, Options) -> + ParsedOpts = parse_factory_options(Options, #tbp_opts{}), + F = fun() -> + case TransportFactory() of + {ok, Transport} -> + thrift_binary_protocol:new( + Transport, + [{strict_read, ParsedOpts#tbp_opts.strict_read}, + {strict_write, ParsedOpts#tbp_opts.strict_write}]); + {error, Error} -> + {error, Error} + end + end, + {ok, F}. + diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_buffered_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_buffered_transport.erl new file mode 100644 index 000000000..e9d3fffa7 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_buffered_transport.erl @@ -0,0 +1,98 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_buffered_transport). + +-behaviour(thrift_transport). + +%% constructor +-export([new/1]). +%% protocol callbacks +-export([read/2, read_exact/2, write/2, flush/1, close/1]). +%% legacy api +-export([new_transport_factory/1]). + + +-record(t_buffered, { + wrapped, + write_buffer +}). + +-type state() :: #t_buffered{}. + + +-spec new(Transport::thrift_transport:t_transport()) -> + thrift_transport:t_transport(). + +new(Wrapped) -> + State = #t_buffered{ + wrapped = Wrapped, + write_buffer = [] + }, + thrift_transport:new(?MODULE, State). + + +-include("thrift_transport_behaviour.hrl"). + + +%% reads data through from the wrapped transport +read(State = #t_buffered{wrapped = Wrapped}, Len) +when is_integer(Len), Len >= 0 -> + {NewState, Response} = thrift_transport:read(Wrapped, Len), + {State#t_buffered{wrapped = NewState}, Response}. + + +%% reads data through from the wrapped transport +read_exact(State = #t_buffered{wrapped = Wrapped}, Len) +when is_integer(Len), Len >= 0 -> + {NewState, Response} = thrift_transport:read_exact(Wrapped, Len), + {State#t_buffered{wrapped = NewState}, Response}. + + +write(State = #t_buffered{write_buffer = Buffer}, Data) -> + {State#t_buffered{write_buffer = [Buffer, Data]}, ok}. + + +flush(State = #t_buffered{wrapped = Wrapped, write_buffer = Buffer}) -> + case iolist_size(Buffer) of + %% if write buffer is empty, do nothing + 0 -> {State, ok}; + _ -> + {Written, Response} = thrift_transport:write(Wrapped, Buffer), + {Flushed, ok} = thrift_transport:flush(Written), + {State#t_buffered{wrapped = Flushed, write_buffer = []}, Response} + end. + + +close(State = #t_buffered{wrapped = Wrapped}) -> + {Closed, Result} = thrift_transport:close(Wrapped), + {State#t_buffered{wrapped = Closed}, Result}. + + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +new_transport_factory(WrapFactory) -> + F = fun() -> + {ok, Wrapped} = WrapFactory(), + new(Wrapped) + end, + {ok, F}. + diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_client.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_client.erl new file mode 100644 index 000000000..1a9cb50a4 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_client.erl @@ -0,0 +1,162 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_client). + +%% API +-export([new/2, call/3, send_call/3, close/1]). + +-include("thrift_constants.hrl"). +-include("thrift_protocol.hrl"). + +-record(tclient, {service, protocol, seqid}). + + +new(Protocol, Service) + when is_atom(Service) -> + {ok, #tclient{protocol = Protocol, + service = Service, + seqid = 0}}. + +-spec call(#tclient{}, atom(), list()) -> {#tclient{}, {ok, any()} | {error, any()}}. +call(Client = #tclient{}, Function, Args) +when is_atom(Function), is_list(Args) -> + case send_function_call(Client, Function, Args) of + {ok, Client1} -> receive_function_result(Client1, Function); + {{error, X}, Client1} -> {Client1, {error, X}}; + Else -> Else + end. + + +%% Sends a function call but does not read the result. This is useful +%% if you're trying to log non-oneway function calls to write-only +%% transports like thrift_disk_log_transport. +-spec send_call(#tclient{}, atom(), list()) -> {#tclient{}, ok}. +send_call(Client = #tclient{}, Function, Args) + when is_atom(Function), is_list(Args) -> + case send_function_call(Client, Function, Args) of + {ok, Client1} -> {Client1, ok}; + Else -> Else + end. + +-spec close(#tclient{}) -> ok. +close(#tclient{protocol=Protocol}) -> + thrift_protocol:close_transport(Protocol). + + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +-spec send_function_call(#tclient{}, atom(), list()) -> {ok | {error, any()}, #tclient{}}. +send_function_call(Client = #tclient{service = Service}, Function, Args) -> + {Params, Reply} = try + {Service:function_info(Function, params_type), Service:function_info(Function, reply_type)} + catch error:function_clause -> {no_function, 0} + end, + MsgType = case Reply of + oneway_void -> ?tMessageType_ONEWAY; + _ -> ?tMessageType_CALL + end, + case Params of + no_function -> + {{error, {no_function, Function}}, Client}; + {struct, PList} when length(PList) =/= length(Args) -> + {{error, {bad_args, Function, Args}}, Client}; + {struct, _PList} -> write_message(Client, Function, Args, Params, MsgType) + end. + +-spec write_message(#tclient{}, atom(), list(), {struct, list()}, integer()) -> + {ok | {error, any()}, #tclient{}}. +write_message(Client = #tclient{protocol = P0, seqid = Seq}, Function, Args, Params, MsgType) -> + try + {P1, ok} = thrift_protocol:write(P0, #protocol_message_begin{ + name = atom_to_list(Function), + type = MsgType, + seqid = Seq + }), + {P2, ok} = thrift_protocol:write(P1, {Params, list_to_tuple([Function|Args])}), + {P3, ok} = thrift_protocol:write(P2, message_end), + {P4, ok} = thrift_protocol:flush_transport(P3), + {ok, Client#tclient{protocol = P4}} + catch + error:{badmatch, {_, {error, _} = Error}} -> {Error, Client} + end. + +-spec receive_function_result(#tclient{}, atom()) -> {#tclient{}, {ok, any()} | {error, any()}}. +receive_function_result(Client = #tclient{service = Service}, Function) -> + ResultType = Service:function_info(Function, reply_type), + read_result(Client, Function, ResultType). + +read_result(Client, _Function, oneway_void) -> + {Client, {ok, ok}}; + +read_result(Client = #tclient{protocol = Proto0, + seqid = SeqId}, + Function, + ReplyType) -> + case thrift_protocol:read(Proto0, message_begin) of + {Proto1, {error, Reason}} -> + NewClient = Client#tclient{protocol = Proto1}, + {NewClient, {error, Reason}}; + {Proto1, MessageBegin} -> + NewClient = Client#tclient{protocol = Proto1}, + case MessageBegin of + #protocol_message_begin{seqid = RetSeqId} when RetSeqId =/= SeqId -> + {NewClient, {error, {bad_seq_id, SeqId}}}; + #protocol_message_begin{type = ?tMessageType_EXCEPTION} -> + handle_application_exception(NewClient); + #protocol_message_begin{type = ?tMessageType_REPLY} -> + handle_reply(NewClient, Function, ReplyType) + end + end. + + +handle_reply(Client = #tclient{protocol = Proto0, + service = Service}, + Function, + ReplyType) -> + {struct, ExceptionFields} = Service:function_info(Function, exceptions), + ReplyStructDef = {struct, [{0, ReplyType}] ++ ExceptionFields}, + {Proto1, {ok, Reply}} = thrift_protocol:read(Proto0, ReplyStructDef), + {Proto2, ok} = thrift_protocol:read(Proto1, message_end), + NewClient = Client#tclient{protocol = Proto2}, + ReplyList = tuple_to_list(Reply), + true = length(ReplyList) == length(ExceptionFields) + 1, + ExceptionVals = tl(ReplyList), + Thrown = [X || X <- ExceptionVals, + X =/= undefined], + case Thrown of + [] when ReplyType == {struct, []} -> + {NewClient, {ok, ok}}; + [] -> + {NewClient, {ok, hd(ReplyList)}}; + [Exception] -> + throw({NewClient, {exception, Exception}}) + end. + +handle_application_exception(Client = #tclient{protocol = Proto0}) -> + {Proto1, {ok, Exception}} = + thrift_protocol:read(Proto0, ?TApplicationException_Structure), + {Proto2, ok} = thrift_protocol:read(Proto1, message_end), + XRecord = list_to_tuple( + ['TApplicationException' | tuple_to_list(Exception)]), + error_logger:error_msg("X: ~p~n", [XRecord]), + true = is_record(XRecord, 'TApplicationException'), + NewClient = Client#tclient{protocol = Proto2}, + throw({NewClient, {exception, XRecord}}). diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_client_util.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_client_util.erl new file mode 100644 index 000000000..1dbe51e9d --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_client_util.erl @@ -0,0 +1,112 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_client_util). + +-export([new/4]). +-export([new_multiplexed/3, new_multiplexed/4]). + +-type service_name() :: nonempty_string(). +-type service_module() :: atom(). +-type multiplexed_service_map() :: [{ServiceName::service_name(), ServiceModule::service_module()}]. + +%% +%% Splits client options into client, protocol, and transport options +%% +%% split_options([Options...]) -> {ProtocolOptions, TransportOptions} +%% +split_options(Options) -> + split_options(Options, [], []). + +split_options([], ProtoIn, TransIn) -> + {ProtoIn, TransIn}; + +split_options([Opt = {OptKey, _} | Rest], ProtoIn, TransIn) + when OptKey =:= strict_read; + OptKey =:= strict_write; + OptKey =:= protocol -> + split_options(Rest, [Opt | ProtoIn], TransIn); + +split_options([Opt = {OptKey, _} | Rest], ProtoIn, TransIn) + when OptKey =:= framed; + OptKey =:= connect_timeout; + OptKey =:= recv_timeout; + OptKey =:= sockopts; + OptKey =:= ssltransport; + OptKey =:= ssloptions-> + split_options(Rest, ProtoIn, [Opt | TransIn]). + + +%% Client constructor for the common-case of socket transports +new(Host, Port, Service, Options) + when is_integer(Port), is_atom(Service), is_list(Options) -> + {ProtoOpts, TransOpts0} = split_options(Options), + + {TransportModule, TransOpts2} = case lists:keytake(ssltransport, 1, TransOpts0) of + {value, {_, true}, TransOpts1} -> {thrift_sslsocket_transport, TransOpts1}; + false -> {thrift_socket_transport, TransOpts0} + end, + + {ProtocolModule, ProtoOpts1} = case lists:keytake(protocol, 1, ProtoOpts) of + {value, {_, compact}, Opts} -> {thrift_compact_protocol, Opts}; + {value, {_, json}, Opts} -> {thrift_json_protocol, Opts}; + {value, {_, binary}, Opts} -> {thrift_binary_protocol, Opts}; + false -> {thrift_binary_protocol, ProtoOpts} + end, + {ok, TransportFactory} = + TransportModule:new_transport_factory(Host, Port, TransOpts2), + + {ok, ProtocolFactory} = ProtocolModule:new_protocol_factory( + TransportFactory, ProtoOpts1), + + case ProtocolFactory() of + {ok, Protocol} -> + thrift_client:new(Protocol, Service); + {error, Error} -> + {error, Error} + end. + +-spec new_multiplexed(Host, Port, Services, Options) -> {ok, ServiceThriftClientList} when + Host :: nonempty_string(), + Port :: non_neg_integer(), + Services :: multiplexed_service_map(), + Options :: list(), + ServiceThriftClientList :: [{ServiceName::list(), ThriftClient::term()}]. +new_multiplexed(Host, Port, Services, Options) when is_integer(Port), + is_list(Services), + is_list(Options) -> + new_multiplexed(thrift_socket_transport:new_transport_factory(Host, Port, Options), Services, Options). + +-spec new_multiplexed(TransportFactoryTuple, Services, Options) -> {ok, ServiceThriftClientList} when + TransportFactoryTuple :: {ok, TransportFactory::term()}, + Services :: multiplexed_service_map(), + Options :: list(), + ServiceThriftClientList :: [{ServiceName::service_name(), ThriftClient::term()}]. +new_multiplexed(TransportFactoryTuple, Services, Options) when is_list(Services), + is_list(Options), + is_tuple(TransportFactoryTuple) -> + {ProtoOpts, _} = split_options(Options), + + {ok, TransportFactory} = TransportFactoryTuple, + + {ok, ProtocolFactory} = thrift_binary_protocol:new_protocol_factory(TransportFactory, ProtoOpts), + + {ok, Protocol} = ProtocolFactory(), + + {ok, [{ServiceName, element(2, thrift_client:new(element(2, thrift_multiplexed_protocol:new(Protocol, ServiceName)), Service))} || {ServiceName, Service} <- Services]}. diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_compact_protocol.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_compact_protocol.erl new file mode 100644 index 000000000..0f1422191 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_compact_protocol.erl @@ -0,0 +1,390 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_compact_protocol). + +-behaviour(thrift_protocol). + +-include("thrift_constants.hrl"). +-include("thrift_protocol.hrl"). + +-export([new/1, new/2, + read/2, + write/2, + flush_transport/1, + close_transport/1, + new_protocol_factory/2 + ]). + +-define(ID_NONE, 16#10000). +-define(CBOOL_NONE, 0). +-define(CBOOL_TRUE, 1). +-define(CBOOL_FALSE, 2). + +-record(t_compact, {transport, + % state for pending boolean fields + read_stack=[], + read_value=?CBOOL_NONE, + write_stack=[], + write_id=?ID_NONE + }). +-type state() :: #t_compact{}. +-include("thrift_protocol_behaviour.hrl"). + +-define(PROTOCOL_ID, 16#82). +-define(VERSION_MASK, 16#1f). +-define(VERSION_1, 16#01). +-define(TYPE_MASK, 16#E0). +-define(TYPE_BITS, 16#07). +-define(TYPE_SHIFT_AMOUNT, 5). + +typeid_to_compact(?tType_STOP) -> 16#0; +typeid_to_compact(?tType_BOOL) -> 16#2; +typeid_to_compact(?tType_I8) -> 16#3; +typeid_to_compact(?tType_I16) -> 16#4; +typeid_to_compact(?tType_I32) -> 16#5; +typeid_to_compact(?tType_I64) -> 16#6; +typeid_to_compact(?tType_DOUBLE) -> 16#7; +typeid_to_compact(?tType_STRING) -> 16#8; +typeid_to_compact(?tType_STRUCT) -> 16#C; +typeid_to_compact(?tType_MAP) -> 16#B; +typeid_to_compact(?tType_SET) -> 16#A; +typeid_to_compact(?tType_LIST) -> 16#9. + +compact_to_typeid(16#0) -> ?tType_STOP; +compact_to_typeid(?CBOOL_FALSE) -> ?tType_BOOL; +compact_to_typeid(?CBOOL_TRUE) -> ?tType_BOOL; +compact_to_typeid(16#7) -> ?tType_DOUBLE; +compact_to_typeid(16#3) -> ?tType_I8; +compact_to_typeid(16#4) -> ?tType_I16; +compact_to_typeid(16#5) -> ?tType_I32; +compact_to_typeid(16#6) -> ?tType_I64; +compact_to_typeid(16#8) -> ?tType_STRING; +compact_to_typeid(16#C) -> ?tType_STRUCT; +compact_to_typeid(16#B) -> ?tType_MAP; +compact_to_typeid(16#A) -> ?tType_SET; +compact_to_typeid(16#9) -> ?tType_LIST. + +bool_to_cbool(Value) when Value -> ?CBOOL_TRUE; +bool_to_cbool(_) -> ?CBOOL_FALSE. +cbool_to_bool(Value) -> Value =:= ?CBOOL_TRUE. + +new(Transport) -> new(Transport, _Options = []). + +new(Transport, _Options) -> + State = #t_compact{transport = Transport}, + thrift_protocol:new(?MODULE, State). + +flush_transport(This = #t_compact{transport = Transport}) -> + {NewTransport, Result} = thrift_transport:flush(Transport), + {This#t_compact{transport = NewTransport}, Result}. + +close_transport(This = #t_compact{transport = Transport}) -> + {NewTransport, Result} = thrift_transport:close(Transport), + {This#t_compact{transport = NewTransport}, Result}. + +%%% +%%% instance methods +%%% + +write_field_begin(This0 = #t_compact{write_stack=[LastId|T]}, CompactType, Id) -> + IdDiff = Id - LastId, + This1 = This0#t_compact{write_stack=[Id|T]}, + case (IdDiff > 0) and (IdDiff < 16) of + true -> write(This1, {byte, (IdDiff bsl 4) bor CompactType}); + false -> + {This2, ok} = write(This1, {byte, CompactType}), + write(This2, {i16, Id}) + end. + +-spec to_zigzag(integer()) -> non_neg_integer(). +to_zigzag(Value) -> 16#FFFFFFFFFFFFFFFF band ((Value bsl 1) bxor (Value bsr 63)). + +-spec from_zigzag(non_neg_integer()) -> integer(). +from_zigzag(Value) -> (Value bsr 1) bxor -(Value band 1). + +-spec to_varint(non_neg_integer(), iolist()) -> iolist(). +to_varint(Value, Acc) when (Value < 16#80) -> [Acc, Value]; +to_varint(Value, Acc) -> + to_varint(Value bsr 7, [Acc, ((Value band 16#7F) bor 16#80)]). + +-spec read_varint(#t_compact{}, non_neg_integer(), non_neg_integer()) -> non_neg_integer(). +read_varint(This0, Acc, Count) -> + {This1, {ok, Byte}} = read(This0, byte), + case (Byte band 16#80) of + 0 -> {This1, {ok, (Byte bsl (7 * Count)) + Acc}}; + _ -> read_varint(This1, ((Byte band 16#7f) bsl (7 * Count)) + Acc, Count + 1) + end. + +write(This0, #protocol_message_begin{ + name = Name, + type = Type, + seqid = Seqid}) -> + {This1, ok} = write(This0, {byte, ?PROTOCOL_ID}), + {This2, ok} = write(This1, {byte, (?VERSION_1 band ?VERSION_MASK) bor (Type bsl ?TYPE_SHIFT_AMOUNT)}), + {This3, ok} = write(This2, {ui32, Seqid}), + {This4, ok} = write(This3, {string, Name}), + {This4, ok}; + +write(This, message_end) -> {This, ok}; + +write(This0, #protocol_field_begin{ + name = _Name, + type = Type, + id = Id}) +when (Type =:= ?tType_BOOL) -> {This0#t_compact{write_id = Id}, ok}; + +write(This0, #protocol_field_begin{ + name = _Name, + type = Type, + id = Id}) -> + write_field_begin(This0, typeid_to_compact(Type), Id); + +write(This, field_stop) -> write(This, {byte, ?tType_STOP}); + +write(This, field_end) -> {This, ok}; + +write(This0, #protocol_map_begin{ + ktype = _Ktype, + vtype = _Vtype, + size = Size}) +when Size =:= 0 -> + write(This0, {byte, 0}); + +write(This0, #protocol_map_begin{ + ktype = Ktype, + vtype = Vtype, + size = Size}) -> + {This1, ok} = write(This0, {ui32, Size}), + write(This1, {byte, (typeid_to_compact(Ktype) bsl 4) bor typeid_to_compact(Vtype)}); + +write(This, map_end) -> {This, ok}; + +write(This0, #protocol_list_begin{ + etype = Etype, + size = Size}) +when Size < 16#f -> + write(This0, {byte, (Size bsl 4) bor typeid_to_compact(Etype)}); + +write(This0, #protocol_list_begin{ + etype = Etype, + size = Size}) -> + {This1, ok} = write(This0, {byte, 16#f0 bor typeid_to_compact(Etype)}), + write(This1, {ui32, Size}); + +write(This, list_end) -> {This, ok}; + +write(This0, #protocol_set_begin{ + etype = Etype, + size = Size}) -> + write(This0, #protocol_list_begin{etype = Etype, size = Size}); + +write(This, set_end) -> {This, ok}; + +write(This = #t_compact{write_stack = Stack}, #protocol_struct_begin{}) -> + {This#t_compact{write_stack = [0|Stack]}, ok}; +write(This = #t_compact{write_stack = [_|T]}, struct_end) -> + {This#t_compact{write_stack = T}, ok}; + +write(This = #t_compact{write_id = ?ID_NONE}, {bool, Value}) -> + write(This, {byte, bool_to_cbool(Value)}); + +write(This0 = #t_compact{write_id = Id}, {bool, Value}) -> + {This1, ok} = write_field_begin(This0, bool_to_cbool(Value), Id), + {This1#t_compact{write_id = ?ID_NONE}, ok}; + +write(This, {byte, Value}) when is_integer(Value) -> + write(This, <>); + +write(This, {i16, Value}) when is_integer(Value) -> write(This, to_varint(to_zigzag(Value), [])); +write(This, {ui32, Value}) when is_integer(Value) -> write(This, to_varint(Value, [])); +write(This, {i32, Value}) when is_integer(Value) -> + write(This, to_varint(to_zigzag(Value), [])); +write(This, {i64, Value}) when is_integer(Value) -> write(This, to_varint(to_zigzag(Value), [])); + +write(This, {double, Double}) -> + write(This, <>); + +write(This0, {string, Str}) when is_list(Str) -> + % TODO: limit length + {This1, ok} = write(This0, {ui32, length(Str)}), + {This2, ok} = write(This1, list_to_binary(Str)), + {This2, ok}; + +write(This0, {string, Bin}) when is_binary(Bin) -> + % TODO: limit length + {This1, ok} = write(This0, {ui32, size(Bin)}), + {This2, ok} = write(This1, Bin), + {This2, ok}; + +%% Data :: iolist() +write(This = #t_compact{transport = Trans}, Data) -> + {NewTransport, Result} = thrift_transport:write(Trans, Data), + {This#t_compact{transport = NewTransport}, Result}. + +%% +%% + +read(This0, message_begin) -> + {This1, {ok, ?PROTOCOL_ID}} = read(This0, ubyte), + {This2, {ok, VerAndType}} = read(This1, ubyte), + ?VERSION_1 = VerAndType band ?VERSION_MASK, + {This3, {ok, SeqId}} = read(This2, ui32), + {This4, {ok, Name}} = read(This3, string), + {This4, #protocol_message_begin{ + name = binary_to_list(Name), + type = (VerAndType bsr ?TYPE_SHIFT_AMOUNT) band ?TYPE_BITS, + seqid = SeqId}}; + +read(This, message_end) -> {This, ok}; + +read(This = #t_compact{read_stack = Stack}, struct_begin) -> + {This#t_compact{read_stack = [0|Stack]}, ok}; +read(This = #t_compact{read_stack = [_H|T]}, struct_end) -> + {This#t_compact{read_stack = T}, ok}; + +read(This0 = #t_compact{read_stack = [LastId|T]}, field_begin) -> + {This1, {ok, Byte}} = read(This0, ubyte), + case Byte band 16#f of + CompactType = ?tType_STOP -> + {This1, #protocol_field_begin{type = CompactType}}; + CompactType -> + {This2, {ok, Id}} = case Byte bsr 4 of + 0 -> read(This1, i16); + IdDiff -> + {This1, {ok, LastId + IdDiff}} + end, + case compact_to_typeid(CompactType) of + ?tType_BOOL -> + {This2#t_compact{read_stack = [Id|T], read_value = cbool_to_bool(CompactType)}, + #protocol_field_begin{type = ?tType_BOOL, id = Id}}; + Type -> + {This2#t_compact{read_stack = [Id|T]}, + #protocol_field_begin{type = Type, id = Id}} + end + end; + +read(This, field_end) -> {This, ok}; + +read(This0, map_begin) -> + {This1, {ok, Size}} = read(This0, ui32), + {This2, {ok, KV}} = case Size of + 0 -> {This1, {ok, 0}}; + _ -> read(This1, ubyte) + end, + {This2, #protocol_map_begin{ktype = compact_to_typeid(KV bsr 4), + vtype = compact_to_typeid(KV band 16#f), + size = Size}}; +read(This, map_end) -> {This, ok}; + +read(This0, list_begin) -> + {This1, {ok, SizeAndType}} = read(This0, ubyte), + {This2, {ok, Size}} = case (SizeAndType bsr 4) band 16#f of + 16#f -> read(This1, ui32); + Else -> {This1, {ok, Else}} + end, + {This2, #protocol_list_begin{etype = compact_to_typeid(SizeAndType band 16#f), + size = Size}}; + +read(This, list_end) -> {This, ok}; + +read(This0, set_begin) -> + {This1, {ok, SizeAndType}} = read(This0, ubyte), + {This2, {ok, Size}} = case (SizeAndType bsr 4) band 16#f of + 16#f -> read(This1, ui32); + Else -> {This1, {ok, Else}} + end, + {This2, #protocol_set_begin{etype = compact_to_typeid(SizeAndType band 16#f), + size = Size}}; + +read(This, set_end) -> {This, ok}; + +read(This0, field_stop) -> + {This1, {ok, ?tType_STOP}} = read(This0, ubyte), + {This1, ok}; + +%% + +read(This0 = #t_compact{read_value = ?CBOOL_NONE}, bool) -> + {This1, {ok, Byte}} = read(This0, ubyte), + {This1, {ok, cbool_to_bool(Byte)}}; + +read(This0 = #t_compact{read_value = Bool}, bool) -> + {This0#t_compact{read_value = ?CBOOL_NONE}, {ok, Bool}}; + +read(This0, ubyte) -> + {This1, {ok, <>}} = read_data(This0, 1), + {This1, {ok, Val}}; + +read(This0, byte) -> + {This1, Bytes} = read_data(This0, 1), + case Bytes of + {ok, <>} -> {This1, {ok, Val}}; + Else -> {This1, Else} + end; + +read(This0, i16) -> + {This1, {ok, Zigzag}} = read_varint(This0, 0, 0), + {This1, {ok, from_zigzag(Zigzag)}}; + +read(This0, ui32) -> read_varint(This0, 0, 0); + +read(This0, i32) -> + {This1, {ok, Zigzag}} = read_varint(This0, 0, 0), + {This1, {ok, from_zigzag(Zigzag)}}; + +read(This0, i64) -> + {This1, {ok, Zigzag}} = read_varint(This0, 0, 0), + {This1, {ok, from_zigzag(Zigzag)}}; + +read(This0, double) -> + {This1, Bytes} = read_data(This0, 8), + case Bytes of + {ok, <>} -> {This1, {ok, Val}}; + Else -> {This1, Else} + end; + +% returns a binary directly, call binary_to_list if necessary +read(This0, string) -> + {This1, {ok, Sz}} = read(This0, ui32), + read_data(This1, Sz). + +-spec read_data(#t_compact{}, non_neg_integer()) -> + {#t_compact{}, {ok, binary()} | {error, _Reason}}. +read_data(This, 0) -> {This, {ok, <<>>}}; +read_data(This = #t_compact{transport = Trans}, Len) when is_integer(Len) andalso Len > 0 -> + {NewTransport, Result} = thrift_transport:read(Trans, Len), + {This#t_compact{transport = NewTransport}, Result}. + + +%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% returns a (fun() -> thrift_protocol()) +new_protocol_factory(TransportFactory, _Options) -> + F = fun() -> + case TransportFactory() of + {ok, Transport} -> + thrift_compact_protocol:new( + Transport, + []); + {error, Error} -> + {error, Error} + end + end, + {ok, F}. diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_disk_log_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_disk_log_transport.erl new file mode 100644 index 000000000..de8ee417b --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_disk_log_transport.erl @@ -0,0 +1,123 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +%%% Todo: this might be better off as a gen_server type of transport +%%% that handles stuff like group commit, similar to TFileTransport +%%% in cpp land +-module(thrift_disk_log_transport). + +-behaviour(thrift_transport). + +%% API +-export([new/2, new_transport_factory/2, new_transport_factory/3]). + +%% thrift_transport callbacks +-export([read/2, write/2, force_flush/1, flush/1, close/1]). + +%% state +-record(dl_transport, {log, + close_on_close = false, + sync_every = infinity, + sync_tref}). +-type state() :: #dl_transport{}. +-include("thrift_transport_behaviour.hrl"). + + +%% Create a transport attached to an already open log. +%% If you'd like this transport to close the disk_log using disk_log:lclose() +%% when the transport is closed, pass a {close_on_close, true} tuple in the +%% Opts list. +new(LogName, Opts) when is_atom(LogName), is_list(Opts) -> + State = parse_opts(Opts, #dl_transport{log = LogName}), + + State2 = + case State#dl_transport.sync_every of + N when is_integer(N), N > 0 -> + {ok, TRef} = timer:apply_interval(N, ?MODULE, force_flush, [State]), + State#dl_transport{sync_tref = TRef}; + _ -> State + end, + + thrift_transport:new(?MODULE, State2). + + +parse_opts([], State) -> + State; +parse_opts([{close_on_close, Bool} | Rest], State) when is_boolean(Bool) -> + parse_opts(Rest, State#dl_transport{close_on_close = Bool}); +parse_opts([{sync_every, Int} | Rest], State) when is_integer(Int), Int > 0 -> + parse_opts(Rest, State#dl_transport{sync_every = Int}). + + +%%%% TRANSPORT IMPLENTATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% disk_log_transport is write-only +read(State, _Len) -> + {State, {error, no_read_from_disk_log}}. + +write(This = #dl_transport{log = Log}, Data) -> + {This, disk_log:balog(Log, erlang:iolist_to_binary(Data))}. + +force_flush(#dl_transport{log = Log}) -> + error_logger:info_msg("~p syncing~n", [?MODULE]), + disk_log:sync(Log). + +flush(This = #dl_transport{log = Log, sync_every = SE}) -> + case SE of + undefined -> % no time-based sync + disk_log:sync(Log); + _Else -> % sync will happen automagically + ok + end, + {This, ok}. + + + + +%% On close, close the underlying log if we're configured to do so. +close(This = #dl_transport{close_on_close = false}) -> + {This, ok}; +close(This = #dl_transport{log = Log}) -> + {This, disk_log:lclose(Log)}. + + +%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +new_transport_factory(Name, ExtraLogOpts) -> + new_transport_factory(Name, ExtraLogOpts, [{close_on_close, true}, + {sync_every, 500}]). + +new_transport_factory(Name, ExtraLogOpts, TransportOpts) -> + F = fun() -> factory_impl(Name, ExtraLogOpts, TransportOpts) end, + {ok, F}. + +factory_impl(Name, ExtraLogOpts, TransportOpts) -> + LogOpts = [{name, Name}, + {format, external}, + {type, wrap} | + ExtraLogOpts], + Log = + case disk_log:open(LogOpts) of + {ok, LogS} -> + LogS; + {repaired, LogS, Info1, Info2} -> + error_logger:info_msg("Disk log ~p repaired: ~p, ~p~n", [LogS, Info1, Info2]), + LogS + end, + new(Log, TransportOpts). diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_file_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_file_transport.erl new file mode 100644 index 000000000..071152b63 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_file_transport.erl @@ -0,0 +1,115 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_file_transport). + +-behaviour(thrift_transport). + +%% constructors +-export([new/1, new/2]). +%% protocol callbacks +-export([read/2, read_exact/2, write/2, flush/1, close/1]). +%% legacy api +-export([new_reader/1]). + + +-record(t_file, { + device, + should_close = true, + mode = write +}). + +-type state() :: #t_file{}. + + +-spec new(Device::file:io_device()) -> + thrift_transport:t_transport(). + +new(Device) -> new(Device, []). + +-spec new(Device::file:io_device(), Opts::list()) -> + thrift_transport:t_transport(). + +%% Device should be opened in raw and binary mode. +new(Device, Opts) when is_list(Opts) -> + State = parse_opts(Opts, #t_file{device = Device}), + thrift_transport:new(?MODULE, State). + + +parse_opts([{should_close, Bool}|Rest], State) +when is_boolean(Bool) -> + parse_opts(Rest, State#t_file{should_close = Bool}); +parse_opts([{mode, Mode}|Rest], State) +when Mode =:= write; Mode =:= read -> + parse_opts(Rest, State#t_file{mode = Mode}); +parse_opts([], State) -> + State. + + +-include("thrift_transport_behaviour.hrl"). + + +read(State = #t_file{device = Device, mode = read}, Len) +when is_integer(Len), Len >= 0 -> + case file:read(Device, Len) of + eof -> {State, {error, eof}}; + {ok, Result} -> {State, {ok, iolist_to_binary(Result)}} + end; +read(State, _) -> + {State, {error, write_mode}}. + + +read_exact(State = #t_file{device = Device, mode = read}, Len) +when is_integer(Len), Len >= 0 -> + case file:read(Device, Len) of + eof -> {State, {error, eof}}; + {ok, Result} -> + case iolist_size(Result) of + X when X < Len -> {State, {error, eof}}; + _ -> {State, {ok, iolist_to_binary(Result)}} + end + end; +read_exact(State, _) -> + {State, {error, write_mode}}. + + +write(State = #t_file{device = Device, mode = write}, Data) -> + {State, file:write(Device, Data)}; +write(State, _) -> + {State, {error, read_mode}}. + + +flush(State = #t_file{device = Device, mode = write}) -> + {State, file:sync(Device)}. + + +close(State = #t_file{device = Device, should_close = SC}) -> + case SC of + true -> {State, file:close(Device)}; + false -> {State, ok} + end. + + +%% legacy api. left for compatibility +new_reader(Filename) -> + case file:open(Filename, [read, binary, {read_ahead, 1024*1024}]) of + {ok, IODevice} -> new(IODevice, [{should_close, true}, {mode, read}]); + Error -> Error + end. + diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_framed_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_framed_transport.erl new file mode 100644 index 000000000..9a5d6af55 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_framed_transport.erl @@ -0,0 +1,126 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_framed_transport). + +-behaviour(thrift_transport). + +%% constructor +-export([new/1]). +%% protocol callbacks +-export([read/2, read_exact/2, write/2, flush/1, close/1]). + + +-record(t_framed, { + wrapped, + read_buffer, + write_buffer +}). + +-type state() :: #t_framed{}. + + +-spec new(Transport::thrift_transport:t_transport()) -> + thrift_transport:t_transport(). + +new(Wrapped) -> + State = #t_framed{ + wrapped = Wrapped, + read_buffer = [], + write_buffer = [] + }, + thrift_transport:new(?MODULE, State). + + +-include("thrift_transport_behaviour.hrl"). + + +read(State = #t_framed{wrapped = Wrapped, read_buffer = Buffer}, Len) +when is_integer(Len), Len >= 0 -> + Binary = iolist_to_binary(Buffer), + case Binary of + <<>> when Len > 0 -> + case next_frame(Wrapped) of + {NewState, {ok, Frame}} -> + NewBinary = iolist_to_binary([Binary, Frame]), + Give = min(iolist_size(NewBinary), Len), + {Result, Remaining} = split_binary(NewBinary, Give), + {State#t_framed{wrapped = NewState, read_buffer = Remaining}, {ok, Result}}; + {NewState, Error} -> + {State#t_framed{wrapped = NewState}, Error} + end; + %% read of zero bytes + <<>> -> {State, {ok, <<>>}}; + %% read buffer is nonempty + _ -> + Give = min(iolist_size(Binary), Len), + {Result, Remaining} = split_binary(Binary, Give), + {State#t_framed{read_buffer = Remaining}, {ok, Result}} + end. + + +read_exact(State = #t_framed{wrapped = Wrapped, read_buffer = Buffer}, Len) +when is_integer(Len), Len >= 0 -> + Binary = iolist_to_binary(Buffer), + case iolist_size(Binary) of + %% read buffer is larger than requested read size + X when X >= Len -> + {Result, Remaining} = split_binary(Binary, Len), + {State#t_framed{read_buffer = Remaining}, {ok, Result}}; + %% read buffer is insufficient for requested read size + _ -> + case next_frame(Wrapped) of + {NewState, {ok, Frame}} -> + read_exact( + State#t_framed{wrapped = NewState, read_buffer = [Buffer, Frame]}, + Len + ); + {NewState, Error} -> + {State#t_framed{wrapped = NewState}, Error} + end + end. + +next_frame(Transport) -> + case thrift_transport:read_exact(Transport, 4) of + {NewState, {ok, <>}} -> + thrift_transport:read_exact(NewState, FrameLength); + Error -> Error + end. + + +write(State = #t_framed{write_buffer = Buffer}, Data) -> + {State#t_framed{write_buffer = [Buffer, Data]}, ok}. + + +flush(State = #t_framed{write_buffer = Buffer, wrapped = Wrapped}) -> + case iolist_size(Buffer) of + %% if write buffer is empty, do nothing + 0 -> {State, ok}; + FrameLen -> + Data = [<>, Buffer], + {Written, Response} = thrift_transport:write(Wrapped, Data), + {Flushed, ok} = thrift_transport:flush(Written), + {State#t_framed{wrapped = Flushed, write_buffer = []}, Response} + end. + + +close(State = #t_framed{wrapped = Wrapped}) -> + {Closed, Result} = thrift_transport:close(Wrapped), + {State#t_framed{wrapped = Closed}, Result}. + diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_http_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_http_transport.erl new file mode 100644 index 000000000..46bcede53 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_http_transport.erl @@ -0,0 +1,116 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_http_transport). + +-behaviour(thrift_transport). + +%% API +-export([new/2, new/3]). + +%% thrift_transport callbacks +-export([write/2, read/2, flush/1, close/1]). + +-record(http_transport, {host, % string() + path, % string() + read_buffer, % iolist() + write_buffer, % iolist() + http_options, % see http(3) + extra_headers % [{str(), str()}, ...] + }). +-type state() :: #http_transport{}. +-include("thrift_transport_behaviour.hrl"). + +new(Host, Path) -> + new(Host, Path, _Options = []). + +%%-------------------------------------------------------------------- +%% Options include: +%% {http_options, HttpOptions} = See http(3) +%% {extra_headers, ExtraHeaders} = List of extra HTTP headers +%%-------------------------------------------------------------------- +new(Host, Path, Options) -> + State1 = #http_transport{host = Host, + path = Path, + read_buffer = [], + write_buffer = [], + http_options = [], + extra_headers = []}, + ApplyOption = + fun + ({http_options, HttpOpts}, State = #http_transport{}) -> + State#http_transport{http_options = HttpOpts}; + ({extra_headers, ExtraHeaders}, State = #http_transport{}) -> + State#http_transport{extra_headers = ExtraHeaders}; + (Other, #http_transport{}) -> + {invalid_option, Other}; + (_, Error) -> + Error + end, + case lists:foldl(ApplyOption, State1, Options) of + State2 = #http_transport{} -> + thrift_transport:new(?MODULE, State2); + Else -> + {error, Else} + end. + +%% Writes data into the buffer +write(State = #http_transport{write_buffer = WBuf}, Data) -> + {State#http_transport{write_buffer = [WBuf, Data]}, ok}. + +%% Flushes the buffer, making a request +flush(State = #http_transport{host = Host, + path = Path, + read_buffer = Rbuf, + write_buffer = Wbuf, + http_options = HttpOptions, + extra_headers = ExtraHeaders}) -> + case iolist_to_binary(Wbuf) of + <<>> -> + %% Don't bother flushing empty buffers. + {State, ok}; + WBinary -> + {ok, {{_Version, 200, _ReasonPhrase}, _Headers, Body}} = + httpc:request(post, + {"http://" ++ Host ++ Path, + [{"User-Agent", "Erlang/thrift_http_transport"} | ExtraHeaders], + "application/x-thrift", + WBinary}, + HttpOptions, + [{body_format, binary}]), + + State1 = State#http_transport{read_buffer = [Rbuf, Body], + write_buffer = []}, + {State1, ok} + end. + +close(State) -> + {State, ok}. + +read(State = #http_transport{read_buffer = RBuf}, Len) when is_integer(Len) -> + %% Pull off Give bytes, return them to the user, leave the rest in the buffer. + Give = min(iolist_size(RBuf), Len), + case iolist_to_binary(RBuf) of + <> -> + Response = {ok, Data}, + State1 = State#http_transport{read_buffer=RBuf1}, + {State1, Response}; + _ -> + {State, {error, 'EOF'}} + end. diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_json_parser.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_json_parser.erl new file mode 100644 index 000000000..4e47f10ec --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_json_parser.erl @@ -0,0 +1,419 @@ +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% The json parser implementation was created by +%% alisdair sullivan based on +%% the jsx json library + +-module(thrift_json_parser). +-export([parser/0, handle_event/2]). + + +-record(config, {strict_utf8 = false :: boolean()}). + + +parser() -> fun(JSON) -> start(JSON, {?MODULE, []}, [], #config{}) end. + + +handle_event(Event, {Handler, State}, _Config) -> {Handler, Handler:handle_event(Event, State)}. + +handle_event(end_json, State) -> lists:reverse([end_json] ++ State); +handle_event(Event, State) -> [Event] ++ State. + + +%% whitespace +-define(space, 16#20). +-define(tab, 16#09). +-define(cr, 16#0D). +-define(newline, 16#0A). + +%% object delimiters +-define(start_object, 16#7B). +-define(end_object, 16#7D). + +%% array delimiters +-define(start_array, 16#5B). +-define(end_array, 16#5D). + +%% kv seperator +-define(comma, 16#2C). +-define(doublequote, 16#22). +-define(singlequote, 16#27). +-define(colon, 16#3A). + +%% string escape sequences +-define(rsolidus, 16#5C). +-define(solidus, 16#2F). + +%% math +-define(zero, 16#30). +-define(decimalpoint, 16#2E). +-define(negative, 16#2D). +-define(positive, 16#2B). + +%% comments +-define(star, 16#2A). + + +%% some useful guards +-define(is_hex(Symbol), + (Symbol >= $a andalso Symbol =< $f) orelse + (Symbol >= $A andalso Symbol =< $F) orelse + (Symbol >= $0 andalso Symbol =< $9) +). + +-define(is_nonzero(Symbol), + Symbol >= $1 andalso Symbol =< $9 +). + +-define(is_whitespace(Symbol), + Symbol =:= ?space; Symbol =:= ?tab; Symbol =:= ?cr; Symbol =:= ?newline +). + + +%% lists are benchmarked to be faster (tho higher in memory usage) than binaries +new_seq() -> []. +new_seq(C) -> [C]. + +acc_seq(Seq, C) when is_list(C) -> lists:reverse(C) ++ Seq; +acc_seq(Seq, C) -> [C] ++ Seq. + +end_seq(Seq) -> unicode:characters_to_binary(lists:reverse(Seq)). + +end_seq(Seq, _) -> end_seq(Seq). + + +start(<<16#ef, 16#bb, 16#bf, Rest/binary>>, Handler, Stack, Config) -> + value(Rest, Handler, Stack, Config); +start(Bin, Handler, Stack, Config) -> + value(Bin, Handler, Stack, Config). + + +value(<>, Handler, Stack, Config) -> + string(Rest, Handler, new_seq(), Stack, Config); +value(<<$t, Rest/binary>>, Handler, Stack, Config) -> + true(Rest, Handler, Stack, Config); +value(<<$f, Rest/binary>>, Handler, Stack, Config) -> + false(Rest, Handler, Stack, Config); +value(<<$n, Rest/binary>>, Handler, Stack, Config) -> + null(Rest, Handler, Stack, Config); +value(<>, Handler, Stack, Config) -> + negative(Rest, Handler, new_seq($-), Stack, Config); +value(<>, Handler, Stack, Config) -> + zero(Rest, Handler, new_seq($0), Stack, Config); +value(<>, Handler, Stack, Config) when ?is_nonzero(S) -> + integer(Rest, Handler, new_seq(S), Stack, Config); +value(<>, Handler, Stack, Config) -> + object(Rest, handle_event(start_object, Handler, Config), [key|Stack], Config); +value(<>, Handler, Stack, Config) -> + array(Rest, handle_event(start_array, Handler, Config), [array|Stack], Config); +value(<>, Handler, Stack, Config) when ?is_whitespace(S) -> + value(Rest, Handler, Stack, Config); +value(_Bin, _Handler, _Stack, _Config) -> + erlang:error(badarg). + + +object(<>, Handler, Stack, Config) -> + string(Rest, Handler, new_seq(), Stack, Config); +object(<>, Handler, [key|Stack], Config) -> + maybe_done(Rest, handle_event(end_object, Handler, Config), Stack, Config); +object(<>, Handler, Stack, Config) when ?is_whitespace(S) -> + object(Rest, Handler, Stack, Config); +object(_Bin, _Handler, _Stack, _Config) -> + erlang:error(badarg). + + +array(<>, Handler, [array|Stack], Config) -> + maybe_done(Rest, handle_event(end_array, Handler, Config), Stack, Config); +array(<>, Handler, Stack, Config) when ?is_whitespace(S) -> + array(Rest, Handler, Stack, Config); +array(Bin, Handler, Stack, Config) -> + value(Bin, Handler, Stack, Config). + + +colon(<>, Handler, [key|Stack], Config) -> + value(Rest, Handler, [object|Stack], Config); +colon(<>, Handler, Stack, Config) when ?is_whitespace(S) -> + colon(Rest, Handler, Stack, Config); +colon(_Bin, _Handler, _Stack, _Config) -> + erlang:error(badarg). + + +key(<>, Handler, Stack, Config) -> + string(Rest, Handler, new_seq(), Stack, Config); +key(<>, Handler, Stack, Config) when ?is_whitespace(S) -> + key(Rest, Handler, Stack, Config); +key(_Bin, _Handler, _Stack, _Config) -> + erlang:error(badarg). + + +%% note that if you encounter an error from string and you can't find the clause that +%% caused it here, it might be in unescape below +string(<>, Handler, Acc, Stack, Config) -> + doublequote(Rest, Handler, Acc, Stack, Config); +string(<>, Handler, Acc, Stack, Config) -> + string(Rest, Handler, acc_seq(Acc, ?solidus), Stack, Config); +string(<>, Handler, Acc, Stack, Config) -> + unescape(Rest, Handler, Acc, Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X >= 16#20, X < 16#2028 -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X == 16#2028; X == 16#2029 -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X > 16#2029, X < 16#d800 -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X > 16#dfff, X < 16#fdd0 -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X > 16#fdef, X < 16#fffe -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X >= 16#10000, X < 16#1fffe -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X >= 16#20000, X < 16#2fffe -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X >= 16#30000, X < 16#3fffe -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X >= 16#40000, X < 16#4fffe -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X >= 16#50000, X < 16#5fffe -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X >= 16#60000, X < 16#6fffe -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X >= 16#70000, X < 16#7fffe -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X >= 16#80000, X < 16#8fffe -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X >= 16#90000, X < 16#9fffe -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X >= 16#a0000, X < 16#afffe -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X >= 16#b0000, X < 16#bfffe -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X >= 16#c0000, X < 16#cfffe -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X >= 16#d0000, X < 16#dfffe -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X >= 16#e0000, X < 16#efffe -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X >= 16#f0000, X < 16#ffffe -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +string(<>, Handler, Acc, Stack, Config) when X >= 16#100000, X < 16#10fffe -> + string(Rest, Handler, acc_seq(Acc, X), Stack, Config); +%% surrogates +string(<<237, X, _, Rest/binary>>, Handler, Acc, Stack, Config=#config{strict_utf8=false}) + when X >= 160 -> + string(Rest, Handler, acc_seq(Acc, 16#fffd), Stack, Config); +%% u+xfffe, u+xffff, control codes and other noncharacters +string(<<_/utf8, Rest/binary>>, Handler, Acc, Stack, Config=#config{strict_utf8=false}) -> + string(Rest, Handler, acc_seq(Acc, 16#fffd), Stack, Config); +%% u+fffe and u+ffff for R14BXX (subsequent runtimes will happily match the +%% preceding clause +string(<<239, 191, X, Rest/binary>>, Handler, Acc, Stack, Config=#config{strict_utf8=false}) + when X == 190; X == 191 -> + string(Rest, Handler, acc_seq(Acc, 16#fffd), Stack, Config); +%% overlong encodings and missing continuations of a 2 byte sequence +string(<>, Handler, Acc, Stack, Config=#config{strict_utf8=false}) + when X >= 192, X =< 223 -> + strip_continuations(Rest, Handler, Acc, Stack, Config, 1); +%% overlong encodings and missing continuations of a 3 byte sequence +string(<>, Handler, Acc, Stack, Config=#config{strict_utf8=false}) + when X >= 224, X =< 239 -> + strip_continuations(Rest, Handler, Acc, Stack, Config, 2); +%% overlong encodings and missing continuations of a 4 byte sequence +string(<>, Handler, Acc, Stack, Config=#config{strict_utf8=false}) + when X >= 240, X =< 247 -> + strip_continuations(Rest, Handler, Acc, Stack, Config, 3); +%% incompletes and unexpected bytes, including orphan continuations +string(<<_, Rest/binary>>, Handler, Acc, Stack, Config=#config{strict_utf8=false}) -> + string(Rest, Handler, acc_seq(Acc, 16#fffd), Stack, Config); +string(_Bin, _Handler, _Acc, _Stack, _Config) -> + erlang:error(badarg). + + +doublequote(Rest, Handler, Acc, [key|_] = Stack, Config) -> + colon(Rest, handle_event({key, end_seq(Acc, Config)}, Handler, Config), Stack, Config); +doublequote(Rest, Handler, Acc, Stack, Config) -> + maybe_done(Rest, handle_event({string, end_seq(Acc, Config)}, Handler, Config), Stack, Config). + + +%% strips continuation bytes after bad utf bytes, guards against both too short +%% and overlong sequences. N is the maximum number of bytes to strip +strip_continuations(<>, Handler, Acc, Stack, Config, 0) -> + string(Rest, Handler, acc_seq(Acc, 16#fffd), Stack, Config); +strip_continuations(<>, Handler, Acc, Stack, Config, N) when X >= 128, X =< 191 -> + strip_continuations(Rest, Handler, Acc, Stack, Config, N - 1); +%% not a continuation byte, insert a replacement character for sequence thus +%% far and dispatch back to string +strip_continuations(<>, Handler, Acc, Stack, Config, _) -> + string(Rest, Handler, acc_seq(Acc, 16#fffd), Stack, Config). + + +%% this all gets really gross and should probably eventually be folded into +%% but for now it fakes being part of string on incompletes and errors +unescape(<<$b, Rest/binary>>, Handler, Acc, Stack, Config) -> + string(Rest, Handler, acc_seq(Acc, $\b), Stack, Config); +unescape(<<$f, Rest/binary>>, Handler, Acc, Stack, Config) -> + string(Rest, Handler, acc_seq(Acc, $\f), Stack, Config); +unescape(<<$n, Rest/binary>>, Handler, Acc, Stack, Config) -> + string(Rest, Handler, acc_seq(Acc, $\n), Stack, Config); +unescape(<<$r, Rest/binary>>, Handler, Acc, Stack, Config) -> + string(Rest, Handler, acc_seq(Acc, $\r), Stack, Config); +unescape(<<$t, Rest/binary>>, Handler, Acc, Stack, Config) -> + string(Rest, Handler, acc_seq(Acc, $\t), Stack, Config); +unescape(<>, Handler, Acc, Stack, Config) -> + string(Rest, Handler, acc_seq(Acc, $\"), Stack, Config); +unescape(<>, Handler, Acc, Stack, Config) -> + string(Rest, Handler, acc_seq(Acc, $\\), Stack, Config); +unescape(<>, Handler, Acc, Stack, Config) -> + string(Rest, Handler, acc_seq(Acc, $/), Stack, Config); +unescape(<<$u, $d, A, B, C, ?rsolidus, $u, $d, X, Y, Z, Rest/binary>>, Handler, Acc, Stack, Config) + when (A == $8 orelse A == $9 orelse A == $a orelse A == $b), + (X == $c orelse X == $d orelse X == $e orelse X == $f), + ?is_hex(B), ?is_hex(C), ?is_hex(Y), ?is_hex(Z) + -> + High = erlang:list_to_integer([$d, A, B, C], 16), + Low = erlang:list_to_integer([$d, X, Y, Z], 16), + Codepoint = (High - 16#d800) * 16#400 + (Low - 16#dc00) + 16#10000, + string(Rest, Handler, acc_seq(Acc, Codepoint), Stack, Config); +unescape(<<$u, $d, A, B, C, ?rsolidus, $u, W, X, Y, Z, Rest/binary>>, Handler, Acc, Stack, Config) + when (A == $8 orelse A == $9 orelse A == $a orelse A == $b), + ?is_hex(B), ?is_hex(C), ?is_hex(W), ?is_hex(X), ?is_hex(Y), ?is_hex(Z) + -> + string(Rest, Handler, acc_seq(Acc, [16#fffd, 16#fffd]), Stack, Config); +unescape(<<$u, A, B, C, D, Rest/binary>>, Handler, Acc, Stack, Config) + when ?is_hex(A), ?is_hex(B), ?is_hex(C), ?is_hex(D) -> + case erlang:list_to_integer([A, B, C, D], 16) of + Codepoint when Codepoint < 16#d800; Codepoint > 16#dfff -> + string(Rest, Handler, acc_seq(Acc, Codepoint), Stack, Config); + _ -> + string(Rest, Handler, acc_seq(Acc, 16#fffd), Stack, Config) + end; +unescape(_Bin, _Handler, _Acc, _Stack, _Config) -> + erlang:error(badarg). + + +%% like in strings, there's some pseudo states in here that will never +%% show up in errors or incompletes. some show up in value, some show +%% up in integer, decimal or exp +negative(<<$0, Rest/binary>>, Handler, Acc, Stack, Config) -> + zero(Rest, Handler, acc_seq(Acc, $0), Stack, Config); +negative(<>, Handler, Acc, Stack, Config) when ?is_nonzero(S) -> + integer(Rest, Handler, acc_seq(Acc, S), Stack, Config); +negative(_Bin, _Handler, _Acc, _Stack, _Config) -> + erlang:error(badarg). + + +zero(<>, Handler, Acc, Stack, Config) -> + decimal(Rest, Handler, acc_seq(Acc, ?decimalpoint), Stack, Config); +zero(<>, Handler, Acc, Stack, Config) when S =:= $e; S =:= $E -> + e(Rest, Handler, acc_seq(Acc, ".0e"), Stack, Config); +zero(Bin, Handler, Acc, Stack, Config) -> + finish_number(Bin, Handler, {zero, Acc}, Stack, Config). + + +integer(<>, Handler, Acc, Stack, Config) when S =:= ?zero; ?is_nonzero(S) -> + integer(Rest, Handler, acc_seq(Acc, S), Stack, Config); +integer(<>, Handler, Acc, Stack, Config) -> + initialdecimal(Rest, Handler, acc_seq(Acc, ?decimalpoint), Stack, Config); +integer(<>, Handler, Acc, Stack, Config) when S =:= $e; S =:= $E -> + e(Rest, Handler, acc_seq(Acc, ".0e"), Stack, Config); +integer(Bin, Handler, Acc, Stack, Config) -> + finish_number(Bin, Handler, {integer, Acc}, Stack, Config). + + +initialdecimal(<>, Handler, Acc, Stack, Config) when S =:= ?zero; ?is_nonzero(S) -> + decimal(Rest, Handler, acc_seq(Acc, S), Stack, Config); +initialdecimal(_Bin, _Handler, _Acc, _Stack, _Config) -> + erlang:error(badarg). + + +decimal(<>, Handler, Acc, Stack, Config) when S =:= ?zero; ?is_nonzero(S) -> + decimal(Rest, Handler, acc_seq(Acc, S), Stack, Config); +decimal(<>, Handler, Acc, Stack, Config) when S =:= $e; S =:= $E -> + e(Rest, Handler, acc_seq(Acc, $e), Stack, Config); +decimal(Bin, Handler, Acc, Stack, Config) -> + finish_number(Bin, Handler, {decimal, Acc}, Stack, Config). + + +e(<>, Handler, Acc, Stack, Config) when S =:= ?zero; ?is_nonzero(S) -> + exp(Rest, Handler, acc_seq(Acc, S), Stack, Config); +e(<>, Handler, Acc, Stack, Config) when Sign =:= ?positive; Sign =:= ?negative -> + ex(Rest, Handler, acc_seq(Acc, Sign), Stack, Config); +e(_Bin, _Handler, _Acc, _Stack, _Config) -> + erlang:error(badarg). + + +ex(<>, Handler, Acc, Stack, Config) when S =:= ?zero; ?is_nonzero(S) -> + exp(Rest, Handler, acc_seq(Acc, S), Stack, Config); +ex(_Bin, _Handler, _Acc, _Stack, _Config) -> + erlang:error(badarg). + + +exp(<>, Handler, Acc, Stack, Config) when S =:= ?zero; ?is_nonzero(S) -> + exp(Rest, Handler, acc_seq(Acc, S), Stack, Config); +exp(Bin, Handler, Acc, Stack, Config) -> + finish_number(Bin, Handler, {exp, Acc}, Stack, Config). + + +finish_number(Rest, Handler, Acc, [], Config) -> + maybe_done(Rest, handle_event(format_number(Acc), Handler, Config), [], Config); +finish_number(Rest, Handler, Acc, Stack, Config) -> + maybe_done(Rest, handle_event(format_number(Acc), Handler, Config), Stack, Config). + + +format_number({zero, Acc}) -> {integer, list_to_integer(lists:reverse(Acc))}; +format_number({integer, Acc}) -> {integer, list_to_integer(lists:reverse(Acc))}; +format_number({decimal, Acc}) -> {float, list_to_float(lists:reverse(Acc))}; +format_number({exp, Acc}) -> {float, list_to_float(lists:reverse(Acc))}. + + +true(<<$r, $u, $e, Rest/binary>>, Handler, Stack, Config) -> + maybe_done(Rest, handle_event({literal, true}, Handler, Config), Stack, Config); +true(_Bin, _Handler, _Stack, _Config) -> + erlang:error(badarg). + + +false(<<$a, $l, $s, $e, Rest/binary>>, Handler, Stack, Config) -> + maybe_done(Rest, handle_event({literal, false}, Handler, Config), Stack, Config); +false(_Bin, _Handler, _Stack, _Config) -> + erlang:error(badarg). + + +null(<<$u, $l, $l, Rest/binary>>, Handler, Stack, Config) -> + maybe_done(Rest, handle_event({literal, null}, Handler, Config), Stack, Config); +null(_Bin, _Handler, _Stack, _Config) -> + erlang:error(badarg). + + +maybe_done(<>, Handler, [], Config) -> + done(Rest, handle_event(end_json, Handler, Config), [], Config); +maybe_done(<>, Handler, [object|Stack], Config) -> + maybe_done(Rest, handle_event(end_object, Handler, Config), Stack, Config); +maybe_done(<>, Handler, [array|Stack], Config) -> + maybe_done(Rest, handle_event(end_array, Handler, Config), Stack, Config); +maybe_done(<>, Handler, [object|Stack], Config) -> + key(Rest, Handler, [key|Stack], Config); +maybe_done(<>, Handler, [array|_] = Stack, Config) -> + value(Rest, Handler, Stack, Config); +maybe_done(<>, Handler, Stack, Config) when ?is_whitespace(S) -> + maybe_done(Rest, Handler, Stack, Config); +maybe_done(_Bin, _Handler, _Stack, _Config) -> + erlang:error(badarg). + + +done(<>, Handler, [], Config) when ?is_whitespace(S) -> + done(Rest, Handler, [], Config); +done(<<>>, {_Handler, State}, [], _Config) -> State; +done(_Bin, _Handler, _Stack, _Config) -> erlang:error(badarg). diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_json_protocol.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_json_protocol.erl new file mode 100644 index 000000000..c5f3da838 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_json_protocol.erl @@ -0,0 +1,567 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% The JSON protocol implementation was created by +%% Peter Neumark based on +%% the binary protocol implementation. + +-module(thrift_json_protocol). + +-behaviour(thrift_protocol). + +-include("thrift_constants.hrl"). +-include("thrift_protocol.hrl"). + +-export([new/1, new/2, + read/2, + write/2, + flush_transport/1, + close_transport/1, + new_protocol_factory/2 + ]). + +-record(json_context, { + % the type of json_context: array or object + type, + % fields read or written + fields_processed = 0 +}). + +-record(json_protocol, { + transport, + context_stack = [], + jsx +}). +-type state() :: #json_protocol{}. +-include("thrift_protocol_behaviour.hrl"). + +-define(VERSION_1, 1). +-define(JSON_DOUBLE_PRECISION, 16). + +typeid_to_json(?tType_BOOL) -> "tf"; +typeid_to_json(?tType_BYTE) -> "i8"; +typeid_to_json(?tType_DOUBLE) -> "dbl"; +typeid_to_json(?tType_I8) -> "i8"; +typeid_to_json(?tType_I16) -> "i16"; +typeid_to_json(?tType_I32) -> "i32"; +typeid_to_json(?tType_I64) -> "i64"; +typeid_to_json(?tType_STRING) -> "str"; +typeid_to_json(?tType_STRUCT) -> "rec"; +typeid_to_json(?tType_MAP) -> "map"; +typeid_to_json(?tType_SET) -> "set"; +typeid_to_json(?tType_LIST) -> "lst". + +json_to_typeid("tf") -> ?tType_BOOL; +json_to_typeid("dbl") -> ?tType_DOUBLE; +json_to_typeid("i8") -> ?tType_I8; +json_to_typeid("i16") -> ?tType_I16; +json_to_typeid("i32") -> ?tType_I32; +json_to_typeid("i64") -> ?tType_I64; +json_to_typeid("str") -> ?tType_STRING; +json_to_typeid("rec") -> ?tType_STRUCT; +json_to_typeid("map") -> ?tType_MAP; +json_to_typeid("set") -> ?tType_SET; +json_to_typeid("lst") -> ?tType_LIST. + +start_context(object) -> "{"; +start_context(array) -> "[". + +end_context(object) -> "}"; +end_context(array) -> "]". + + +new(Transport) -> + new(Transport, _Options = []). + +new(Transport, _Options) -> + State = #json_protocol{transport = Transport}, + thrift_protocol:new(?MODULE, State). + +flush_transport(This = #json_protocol{transport = Transport}) -> + {NewTransport, Result} = thrift_transport:flush(Transport), + {This#json_protocol{ + transport = NewTransport, + context_stack = [] + }, Result}. + +close_transport(This = #json_protocol{transport = Transport}) -> + {NewTransport, Result} = thrift_transport:close(Transport), + {This#json_protocol{ + transport = NewTransport, + context_stack = [], + jsx = undefined + }, Result}. + +%%% +%%% instance methods +%%% +% places a new context on the stack: +write(#json_protocol{context_stack = Stack} = State0, {enter_context, Type}) -> + {State1, ok} = write_values(State0, [{context_pre_item, false}]), + State2 = State1#json_protocol{context_stack = [ + #json_context{type=Type}|Stack]}, + write_values(State2, [list_to_binary(start_context(Type))]); + +% removes the topmost context from stack +write(#json_protocol{context_stack = [CurrCtxt|Stack]} = State0, {exit_context}) -> + Type = CurrCtxt#json_context.type, + State1 = State0#json_protocol{context_stack = Stack}, + write_values(State1, [ + list_to_binary(end_context(Type)), + {context_post_item, false} + ]); + +% writes necessary prelude to field or container depending on current context +write(#json_protocol{context_stack = []} = This0, + {context_pre_item, _}) -> {This0, ok}; +write(#json_protocol{context_stack = [Context|_CtxtTail]} = This0, + {context_pre_item, MayNeedQuotes}) -> + FieldNo = Context#json_context.fields_processed, + CtxtType = Context#json_context.type, + Rem = FieldNo rem 2, + case {CtxtType, FieldNo, Rem, MayNeedQuotes} of + {array, N, _, _} when N > 0 -> % array element (not first) + write(This0, <<",">>); + {object, 0, _, true} -> % non-string object key (first) + write(This0, <<"\"">>); + {object, N, 0, true} when N > 0 -> % non-string object key (not first) + write(This0, <<",\"">>); + {object, N, 0, false} when N > 0-> % string object key (not first) + write(This0, <<",">>); + _ -> % no pre-field necessary + {This0, ok} + end; + +% writes necessary postlude to field or container depending on current context +write(#json_protocol{context_stack = []} = This0, + {context_post_item, _}) -> {This0, ok}; +write(#json_protocol{context_stack = [Context|CtxtTail]} = This0, + {context_post_item, MayNeedQuotes}) -> + FieldNo = Context#json_context.fields_processed, + CtxtType = Context#json_context.type, + Rem = FieldNo rem 2, + {This1, ok} = case {CtxtType, Rem, MayNeedQuotes} of + {object, 0, true} -> % non-string object key + write(This0, <<"\":">>); + {object, 0, false} -> % string object key + write(This0, <<":">>); + _ -> % no pre-field necessary + {This0, ok} + end, + NewContext = Context#json_context{fields_processed = FieldNo + 1}, + {This1#json_protocol{context_stack=[NewContext|CtxtTail]}, ok}; + +write(This0, #protocol_message_begin{ + name = Name, + type = Type, + seqid = Seqid}) -> + write_values(This0, [ + {enter_context, array}, + {i32, ?VERSION_1}, + {string, Name}, + {i32, Type}, + {i32, Seqid} + ]); + +write(This, message_end) -> + write_values(This, [{exit_context}]); + +% Example field expression: "1":{"dbl":3.14} +write(This0, #protocol_field_begin{ + name = _Name, + type = Type, + id = Id}) -> + write_values(This0, [ + % entering 'outer' object + {i16, Id}, + % entering 'outer' object + {enter_context, object}, + {string, typeid_to_json(Type)} + ]); + +write(This, field_stop) -> + {This, ok}; + +write(This, field_end) -> + write_values(This,[{exit_context}]); + +% Example message with map: [1,"testMap",1,0,{"1":{"map":["i32","i32",3,{"7":77,"8":88,"9":99}]}}] +write(This0, #protocol_map_begin{ + ktype = Ktype, + vtype = Vtype, + size = Size}) -> + write_values(This0, [ + {enter_context, array}, + {string, typeid_to_json(Ktype)}, + {string, typeid_to_json(Vtype)}, + {i32, Size}, + {enter_context, object} + ]); + +write(This, map_end) -> + write_values(This,[ + {exit_context}, + {exit_context} + ]); + +write(This0, #protocol_list_begin{ + etype = Etype, + size = Size}) -> + write_values(This0, [ + {enter_context, array}, + {string, typeid_to_json(Etype)}, + {i32, Size} + ]); + +write(This, list_end) -> + write_values(This,[ + {exit_context} + ]); + +% example message with set: [1,"testSet",1,0,{"1":{"set":["i32",3,1,2,3]}}] +write(This0, #protocol_set_begin{ + etype = Etype, + size = Size}) -> + write_values(This0, [ + {enter_context, array}, + {string, typeid_to_json(Etype)}, + {i32, Size} + ]); + +write(This, set_end) -> + write_values(This,[ + {exit_context} + ]); +% example message with struct: [1,"testStruct",1,0,{"1":{"rec":{"1":{"str":"worked"},"4":{"i8":1},"9":{"i32":1073741824},"11":{"i64":1152921504606847000}}}}] +write(This, #protocol_struct_begin{}) -> + write_values(This, [ + {enter_context, object} + ]); + +write(This, struct_end) -> + write_values(This,[ + {exit_context} + ]); + +write(This, {bool, true}) -> write_values(This, [ + {context_pre_item, true}, + <<"true">>, + {context_post_item, true} + ]); + +write(This, {bool, false}) -> write_values(This, [ + {context_pre_item, true}, + <<"false">>, + {context_post_item, true} + ]); + +write(This, {byte, Byte}) -> write_values(This, [ + {context_pre_item, true}, + list_to_binary(integer_to_list(Byte)), + {context_post_item, true} + ]); + +write(This, {i16, I16}) -> + write(This, {byte, I16}); + +write(This, {i32, I32}) -> + write(This, {byte, I32}); + +write(This, {i64, I64}) -> + write(This, {byte, I64}); + +write(This, {double, Double}) -> write_values(This, [ + {context_pre_item, true}, + list_to_binary(io_lib:format("~.*f", [?JSON_DOUBLE_PRECISION,Double])), + {context_post_item, true} + ]); + +write(This0, {string, Str}) -> write_values(This0, [ + {context_pre_item, false}, + case is_binary(Str) of + true -> Str; + false -> <<"\"", (list_to_binary(Str))/binary, "\"">> + end, + {context_post_item, false} + ]); + +%% TODO: binary fields should be base64 encoded? + +%% Data :: iolist() +write(This = #json_protocol{transport = Trans}, Data) -> + %io:format("Data ~p Ctxt ~p~n~n", [Data, This#json_protocol.context_stack]), + {NewTransport, Result} = thrift_transport:write(Trans, Data), + {This#json_protocol{transport = NewTransport}, Result}. + +write_values(This0, ValueList) -> + FinalState = lists:foldl( + fun(Val, ThisIn) -> + {ThisOut, ok} = write(ThisIn, Val), + ThisOut + end, + This0, + ValueList), + {FinalState, ok}. + +%% I wish the erlang version of the transport interface included a +%% read_all function (like eg. the java implementation). Since it doesn't, +%% here's my version (even though it probably shouldn't be in this file). +%% +%% The resulting binary is immediately send to the JSX stream parser. +%% Subsequent calls to read actually operate on the events returned by JSX. +read_all(#json_protocol{transport = Transport0} = State) -> + {Transport1, Bin} = read_all_1(Transport0, []), + P = thrift_json_parser:parser(), + [First|Rest] = P(Bin), + State#json_protocol{ + transport = Transport1, + jsx = {event, First, Rest} + }. + +read_all_1(Transport0, IoList) -> + {Transport1, Result} = thrift_transport:read(Transport0, 1), + case Result of + {ok, <<>>} -> % nothing read: assume we're done + {Transport1, iolist_to_binary(lists:reverse(IoList))}; + {ok, Data} -> % character successfully read; read more + read_all_1(Transport1, [Data|IoList]); + {error, 'EOF'} -> % we're done + {Transport1, iolist_to_binary(lists:reverse(IoList))} + end. + +% Expect reads an event from the JSX event stream. It receives an event or data +% type as input. Comparing the read event from the one is was passed, it +% returns an error if something other than the expected value is encountered. +% Expect also maintains the context stack in #json_protocol. +expect(#json_protocol{jsx={event, {Type, Data}=Ev, [Next|Rest]}}=State, ExpectedType) -> + NextState = State#json_protocol{jsx={event, Next, Rest}}, + case Type == ExpectedType of + true -> + {NextState, {ok, convert_data(Type, Data)}}; + false -> + {NextState, {error, {unexpected_json_event, Ev}}} + end; + +expect(#json_protocol{jsx={event, Event, Next}}=State, ExpectedEvent) -> + expect(State#json_protocol{jsx={event, {Event, none}, Next}}, ExpectedEvent). + +convert_data(integer, I) -> list_to_integer(I); +convert_data(float, F) -> list_to_float(F); +convert_data(_, D) -> D. + +expect_many(State, ExpectedList) -> + expect_many_1(State, ExpectedList, [], ok). + +expect_many_1(State, [], ResultList, Status) -> + {State, {Status, lists:reverse(ResultList)}}; +expect_many_1(State, [Expected|ExpTail], ResultList, _PrevStatus) -> + {State1, {Status, Data}} = expect(State, Expected), + NewResultList = [Data|ResultList], + case Status of + % in case of error, end prematurely + error -> expect_many_1(State1, [], NewResultList, Status); + ok -> expect_many_1(State1, ExpTail, NewResultList, Status) + end. + +% wrapper around expect to make life easier for container opening/closing functions +expect_nodata(This, ExpectedList) -> + case expect_many(This, ExpectedList) of + {State, {ok, _}} -> + {State, ok}; + Error -> + Error + end. + +read_field(#json_protocol{jsx={event, Field, [Next|Rest]}} = State) -> + NewState = State#json_protocol{jsx={event, Next, Rest}}, + {NewState, Field}. + +read(This0, message_begin) -> + % call read_all to get the contents of the transport buffer into JSX. + This1 = read_all(This0), + case expect_many(This1, + [start_array, integer, string, integer, integer]) of + {This2, {ok, [_, Version, Name, Type, SeqId]}} -> + case Version =:= ?VERSION_1 of + true -> + {This2, #protocol_message_begin{name = Name, + type = Type, + seqid = SeqId}}; + false -> + {This2, {error, no_json_protocol_version}} + end; + Other -> Other + end; + +read(This, message_end) -> + expect_nodata(This, [end_array]); + +read(This, struct_begin) -> + expect_nodata(This, [start_object]); + +read(This, struct_end) -> + expect_nodata(This, [end_object]); + +read(This0, field_begin) -> + {This1, Read} = expect_many(This0, + [%field id + key, + % {} surrounding field + start_object, + % type of field + key]), + case Read of + {ok, [FieldIdStr, _, FieldType]} -> + {This1, #protocol_field_begin{ + type = json_to_typeid(FieldType), + id = list_to_integer(FieldIdStr)}}; % TODO: do we need to wrap this in a try/catch? + {error,[{unexpected_json_event, {end_object,none}}]} -> + {This1, #protocol_field_begin{type = ?tType_STOP}}; + Other -> + io:format("**** OTHER branch selected ****"), + {This1, Other} + end; + +read(This, field_end) -> + expect_nodata(This, [end_object]); + +% Example message with map: [1,"testMap",1,0,{"1":{"map":["i32","i32",3,{"7":77,"8":88,"9":99}]}}] +read(This0, map_begin) -> + case expect_many(This0, + [start_array, + % key type + string, + % value type + string, + % size + integer, + % the following object contains the map + start_object]) of + {This1, {ok, [_, Ktype, Vtype, Size, _]}} -> + {This1, #protocol_map_begin{ktype = Ktype, + vtype = Vtype, + size = Size}}; + Other -> Other + end; + +read(This, map_end) -> + expect_nodata(This, [end_object, end_array]); + +read(This0, list_begin) -> + case expect_many(This0, + [start_array, + % element type + string, + % size + integer]) of + {This1, {ok, [_, Etype, Size]}} -> + {This1, #protocol_list_begin{ + etype = Etype, + size = Size}}; + Other -> Other + end; + +read(This, list_end) -> + expect_nodata(This, [end_array]); + +% example message with set: [1,"testSet",1,0,{"1":{"set":["i32",3,1,2,3]}}] +read(This0, set_begin) -> + case expect_many(This0, + [start_array, + % element type + string, + % size + integer]) of + {This1, {ok, [_, Etype, Size]}} -> + {This1, #protocol_set_begin{ + etype = Etype, + size = Size}}; + Other -> Other + end; + +read(This, set_end) -> + expect_nodata(This, [end_array]); + +read(This0, field_stop) -> + {This0, ok}; +%% + +read(This0, bool) -> + {This1, Field} = read_field(This0), + Value = case Field of + {literal, I} -> + {ok, I}; + _Other -> + {error, unexpected_event_for_boolean} + end, + {This1, Value}; + +read(This0, byte) -> + {This1, Field} = read_field(This0), + Value = case Field of + {key, K} -> + {ok, list_to_integer(K)}; + {integer, I} -> + {ok, list_to_integer(I)}; + _Other -> + {error, unexpected_event_for_integer} + end, + {This1, Value}; + +read(This0, i16) -> + read(This0, byte); + +read(This0, i32) -> + read(This0, byte); + +read(This0, i64) -> + read(This0, byte); + +read(This0, double) -> + {This1, Field} = read_field(This0), + Value = case Field of + {float, I} -> + {ok, list_to_float(I)}; + _Other -> + {error, unexpected_event_for_double} + end, + {This1, Value}; + +% returns a binary directly, call binary_to_list if necessary +read(This0, string) -> + {This1, Field} = read_field(This0), + Value = case Field of + {string, I} -> + {ok, I}; + {key, J} -> + {ok, J}; + _Other -> + {error, unexpected_event_for_string} + end, + {This1, Value}. + +%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% returns a (fun() -> thrift_protocol()) +new_protocol_factory(TransportFactory, _Options) -> + % Only strice read/write are implemented + F = fun() -> + {ok, Transport} = TransportFactory(), + thrift_json_protocol:new(Transport, []) + end, + {ok, F}. diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_membuffer_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_membuffer_transport.erl new file mode 100644 index 000000000..be9acb23a --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_membuffer_transport.erl @@ -0,0 +1,83 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_membuffer_transport). + +-behaviour(thrift_transport). + +%% constructors +-export([new/0, new/1]). +%% protocol callbacks +-export([read/2, read_exact/2, write/2, flush/1, close/1]). + + +-record(t_membuffer, { + buffer = [] +}). + +-type state() :: #t_membuffer{}. + + +-spec new() -> thrift_transport:t_transport(). + +new() -> new([]). + +-spec new(Buf::iodata()) -> thrift_transport:t_transport(). + +new(Buf) when is_list(Buf) -> + State = #t_membuffer{buffer = Buf}, + thrift_transport:new(?MODULE, State); +new(Buf) when is_binary(Buf) -> + State = #t_membuffer{buffer = [Buf]}, + thrift_transport:new(?MODULE, State). + + +-include("thrift_transport_behaviour.hrl"). + + +read(State = #t_membuffer{buffer = Buf}, Len) +when is_integer(Len), Len >= 0 -> + Binary = iolist_to_binary(Buf), + Give = min(iolist_size(Binary), Len), + {Result, Remaining} = split_binary(Binary, Give), + {State#t_membuffer{buffer = Remaining}, {ok, Result}}. + + +read_exact(State = #t_membuffer{buffer = Buf}, Len) +when is_integer(Len), Len >= 0 -> + Binary = iolist_to_binary(Buf), + case iolist_size(Binary) of + X when X >= Len -> + {Result, Remaining} = split_binary(Binary, Len), + {State#t_membuffer{buffer = Remaining}, {ok, Result}}; + _ -> + {State, {error, eof}} + end. + + +write(State = #t_membuffer{buffer = Buf}, Data) +when is_list(Data); is_binary(Data) -> + {State#t_membuffer{buffer = [Buf, Data]}, ok}. + + +flush(State) -> {State, ok}. + + +close(State) -> {State, ok}. + diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_memory_buffer.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_memory_buffer.erl new file mode 100644 index 000000000..6a59ea56d --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_memory_buffer.erl @@ -0,0 +1,47 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_memory_buffer). + +-behaviour(thrift_transport). + +%% constructors +-export([new/0, new/1]). +%% protocol callbacks +-export([read/2, write/2, flush/1, close/1]). +%% legacy api +-export([new_transport_factory/0]). + + +%% wrapper around thrift_membuffer_transport for legacy reasons + +new() -> thrift_membuffer_transport:new(). + +new(State) -> thrift_membuffer_transport:new(State). + +new_transport_factory() -> {ok, fun() -> new() end}. + +write(State, Data) -> thrift_membuffer_transport:write(State, Data). + +read(State, Data) -> thrift_membuffer_transport:read(State, Data). + +flush(State) -> thrift_membuffer_transport:flush(State). + +close(State) -> thrift_membuffer_transport:close(State). + diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_multiplexed_map_wrapper.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_multiplexed_map_wrapper.erl new file mode 100644 index 000000000..34c5e95f5 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_multiplexed_map_wrapper.erl @@ -0,0 +1,57 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_multiplexed_map_wrapper). + +-export([ + new/0 + ,store/3 + ,find/2 + ,fetch/2 + ]). + +-type service_handler() :: nonempty_string(). +-type module_() :: atom(). +-type service_handler_map() :: [{ServiceHandler::service_handler(), Module::module_()}]. + +-spec new() -> service_handler_map(). +new() -> + orddict:new(). + +-spec store(ServiceHandler, Module, Map) -> NewMap when + ServiceHandler :: service_handler(), + Module :: module_(), + Map :: service_handler_map(), + NewMap :: service_handler_map(). +store(ServiceHandler, Module, Map) -> + orddict:store(ServiceHandler, Module, Map). + +-spec find(ServiceHandler, Map) -> {ok, Module} | error when + ServiceHandler :: service_handler(), + Module :: module_(), + Map :: service_handler_map(). +find(ServiceHandler, Map) -> + orddict:find(ServiceHandler, Map). + +-spec fetch(ServiceHandler, Map) -> Module when + ServiceHandler :: service_handler(), + Module :: module_(), + Map :: service_handler_map(). +fetch(ServiceHandler, Map) -> + orddict:fetch(ServiceHandler, Map). diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_multiplexed_protocol.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_multiplexed_protocol.erl new file mode 100644 index 000000000..5f7b70c80 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_multiplexed_protocol.erl @@ -0,0 +1,83 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_multiplexed_protocol). + +-behaviour(thrift_protocol). + +-include("thrift_constants.hrl"). +-include("thrift_protocol.hrl"). + +-include("thrift_protocol_behaviour.hrl"). + +-export([new/2, + read/2, + write/2, + flush_transport/1, + close_transport/1 + ]). + +-record(protocol, {module, data}). +-type protocol() :: #protocol{}. + +-record (multiplexed_protocol, {protocol_module_to_decorate::atom(), + protocol_data_to_decorate::term(), + service_name::nonempty_string()}). + +-type state() :: #multiplexed_protocol{}. + +-spec new(ProtocolToDecorate::protocol(), ServiceName::nonempty_string()) -> {ok, Protocol::protocol()}. +new(ProtocolToDecorate, ServiceName) when is_record(ProtocolToDecorate, protocol), + is_list(ServiceName) -> + State = #multiplexed_protocol{protocol_module_to_decorate = ProtocolToDecorate#protocol.module, + protocol_data_to_decorate = ProtocolToDecorate#protocol.data, + service_name = ServiceName}, + thrift_protocol:new(?MODULE, State). + +flush_transport(State = #multiplexed_protocol{protocol_module_to_decorate = ProtocolModuleToDecorate, + protocol_data_to_decorate = State0}) -> + {State1, ok} = ProtocolModuleToDecorate:flush_transport(State0), + {State#multiplexed_protocol{protocol_data_to_decorate = State1}, ok}. + +close_transport(State = #multiplexed_protocol{protocol_module_to_decorate = ProtocolModuleToDecorate, + protocol_data_to_decorate = State0}) -> + {State1, ok} = ProtocolModuleToDecorate:close_transport(State0), + {State#multiplexed_protocol{protocol_data_to_decorate = State1}, ok}. + +write(State = #multiplexed_protocol{protocol_module_to_decorate = ProtocolModuleToDecorate, + protocol_data_to_decorate = State0, + service_name = ServiceName}, + Message = #protocol_message_begin{name = Name}) -> + {State1, ok} = ProtocolModuleToDecorate:write(State0, + Message#protocol_message_begin{name=ServiceName ++ + ?MULTIPLEXED_SERVICE_SEPARATOR ++ + Name}), + {State#multiplexed_protocol{protocol_data_to_decorate = State1}, ok}; + +write(State = #multiplexed_protocol{protocol_module_to_decorate = ProtocolModuleToDecorate, + protocol_data_to_decorate = State0}, + Message) -> + {State1, ok} = ProtocolModuleToDecorate:write(State0, Message), + {State#multiplexed_protocol{protocol_data_to_decorate = State1}, ok}. + +read(State = #multiplexed_protocol{protocol_module_to_decorate = ProtocolModuleToDecorate, + protocol_data_to_decorate = State0}, + Message) -> + {State1, Result} = ProtocolModuleToDecorate:read(State0, Message), + {State#multiplexed_protocol{protocol_data_to_decorate = State1}, Result}. diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_processor.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_processor.erl new file mode 100644 index 000000000..5c9f26f52 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_processor.erl @@ -0,0 +1,219 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_processor). + +-export([init/1]). + +-include("thrift_constants.hrl"). +-include("thrift_protocol.hrl"). + +-record(thrift_processor, {handler, protocol, service}). + +init({_Server, ProtoGen, Service, Handler}) when is_function(ProtoGen, 0) -> + {ok, Proto} = ProtoGen(), + loop(#thrift_processor{protocol = Proto, + service = Service, + handler = Handler}). + +loop(State0 = #thrift_processor{protocol = Proto0, + handler = Handler, + service = Service}) -> + + {Proto1, MessageBegin} = thrift_protocol:read(Proto0, message_begin), + State1 = State0#thrift_processor{protocol = Proto1}, + + ErrorHandler = fun + (HandlerModules) when is_list(HandlerModules) -> thrift_multiplexed_map_wrapper:fetch(?MULTIPLEXED_ERROR_HANDLER_KEY, HandlerModules); + (HandlerModule) -> HandlerModule + end, + + case MessageBegin of + + #protocol_message_begin{name = Function, + type = Type, + seqid = Seqid} when Type =:= ?tMessageType_CALL; Type =:= ?tMessageType_ONEWAY -> + case string:tokens(Function, ?MULTIPLEXED_SERVICE_SEPARATOR) of + [ServiceName, FunctionName] -> + ServiceModule = thrift_multiplexed_map_wrapper:fetch(ServiceName, Service), + ServiceHandler = thrift_multiplexed_map_wrapper:fetch(ServiceName, Handler), + case handle_function(State1#thrift_processor{service=ServiceModule, handler=ServiceHandler}, list_to_atom(FunctionName), Seqid) of + {State2, ok} -> loop(State2#thrift_processor{service=Service, handler=Handler}); + {_State2, {error, Reason}} -> + apply(ErrorHandler(Handler), handle_error, [list_to_atom(Function), Reason]), + thrift_protocol:close_transport(Proto1), + ok + end; + _ -> + case handle_function(State1, list_to_atom(Function), Seqid) of + {State2, ok} -> loop(State2); + {_State2, {error, Reason}} -> + apply(ErrorHandler(Handler), handle_error, [list_to_atom(Function), Reason]), + thrift_protocol:close_transport(Proto1), + ok + end + end; + {error, timeout = Reason} -> + apply(ErrorHandler(Handler), handle_error, [undefined, Reason]), + thrift_protocol:close_transport(Proto1), + ok; + {error, closed = Reason} -> + %% error_logger:info_msg("Client disconnected~n"), + apply(ErrorHandler(Handler), handle_error, [undefined, Reason]), + thrift_protocol:close_transport(Proto1), + exit(shutdown); + {error, Reason} -> + apply(ErrorHandler(Handler), handle_error, [undefined, Reason]), + thrift_protocol:close_transport(Proto1), + exit(shutdown) + end. + +handle_function(State0=#thrift_processor{protocol = Proto0, + handler = Handler, + service = Service}, + Function, + Seqid) -> + InParams = Service:function_info(Function, params_type), + + {Proto1, {ok, Params}} = thrift_protocol:read(Proto0, InParams), + State1 = State0#thrift_processor{protocol = Proto1}, + + try + Result = Handler:handle_function(Function, Params), + %% {Micro, Result} = better_timer(Handler, handle_function, [Function, Params]), + %% error_logger:info_msg("Processed ~p(~p) in ~.4fms~n", + %% [Function, Params, Micro/1000.0]), + handle_success(State1, Function, Result, Seqid) + catch + Type:Data when Type =:= throw orelse Type =:= error -> + handle_function_catch(State1, Function, Type, Data, Seqid) + end. + +handle_function_catch(State = #thrift_processor{service = Service}, + Function, ErrType, ErrData, Seqid) -> + IsOneway = Service:function_info(Function, reply_type) =:= oneway_void, + + case {ErrType, ErrData} of + _ when IsOneway -> + Stack = erlang:get_stacktrace(), + error_logger:warning_msg( + "oneway void ~p threw error which must be ignored: ~p", + [Function, {ErrType, ErrData, Stack}]), + {State, ok}; + + {throw, Exception} when is_tuple(Exception), size(Exception) > 0 -> + %error_logger:warning_msg("~p threw exception: ~p~n", [Function, Exception]), + handle_exception(State, Function, Exception, Seqid); + % we still want to accept more requests from this client + + {error, Error} -> + handle_error(State, Function, Error, Seqid) + end. + +handle_success(State = #thrift_processor{service = Service}, + Function, + Result, + Seqid) -> + ReplyType = Service:function_info(Function, reply_type), + StructName = atom_to_list(Function) ++ "_result", + + case Result of + {reply, ReplyData} -> + Reply = {{struct, [{0, ReplyType}]}, {StructName, ReplyData}}, + send_reply(State, Function, ?tMessageType_REPLY, Reply, Seqid); + + ok when ReplyType == {struct, []} -> + send_reply(State, Function, ?tMessageType_REPLY, {ReplyType, {StructName}}, Seqid); + + ok when ReplyType == oneway_void -> + %% no reply for oneway void + {State, ok} + end. + +handle_exception(State = #thrift_processor{service = Service}, + Function, + Exception, + Seqid) -> + ExceptionType = element(1, Exception), + %% Fetch a structure like {struct, [{-2, {struct, {Module, Type}}}, + %% {-3, {struct, {Module, Type}}}]} + + ReplySpec = Service:function_info(Function, exceptions), + {struct, XInfo} = ReplySpec, + + true = is_list(XInfo), + + %% Assuming we had a type1 exception, we'd get: [undefined, Exception, undefined] + %% e.g.: [{-1, type0}, {-2, type1}, {-3, type2}] + ExceptionList = [case Type of + ExceptionType -> Exception; + _ -> undefined + end + || {_Fid, {struct, {_Module, Type}}} <- XInfo], + + ExceptionTuple = list_to_tuple([Function | ExceptionList]), + + % Make sure we got at least one defined + case lists:all(fun(X) -> X =:= undefined end, ExceptionList) of + true -> + handle_unknown_exception(State, Function, Exception, Seqid); + false -> + send_reply(State, Function, ?tMessageType_REPLY, {ReplySpec, ExceptionTuple}, Seqid) + end. + +%% +%% Called when an exception has been explicitly thrown by the service, but it was +%% not one of the exceptions that was defined for the function. +%% +handle_unknown_exception(State, Function, Exception, Seqid) -> + handle_error(State, Function, {exception_not_declared_as_thrown, + Exception}, Seqid). + +handle_error(State, Function, Error, Seqid) -> + Stack = erlang:get_stacktrace(), + error_logger:error_msg("~p had an error: ~p~n", [Function, {Error, Stack}]), + + Message = + case application:get_env(thrift, exceptions_include_traces) of + {ok, true} -> + lists:flatten(io_lib:format("An error occurred: ~p~n", + [{Error, Stack}])); + _ -> + "An unknown handler error occurred." + end, + Reply = {?TApplicationException_Structure, + #'TApplicationException'{ + message = Message, + type = ?TApplicationException_UNKNOWN}}, + send_reply(State, Function, ?tMessageType_EXCEPTION, Reply, Seqid). + +send_reply(State = #thrift_processor{protocol = Proto0}, Function, ReplyMessageType, Reply, Seqid) -> + try + {Proto1, ok} = thrift_protocol:write(Proto0, #protocol_message_begin{ + name = atom_to_list(Function), + type = ReplyMessageType, + seqid = Seqid}), + {Proto2, ok} = thrift_protocol:write(Proto1, Reply), + {Proto3, ok} = thrift_protocol:write(Proto2, message_end), + {Proto4, ok} = thrift_protocol:flush_transport(Proto3), + {State#thrift_processor{protocol = Proto4}, ok} + catch + error:{badmatch, {_, {error, _} = Error}} -> + {State, Error} + end. diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_protocol.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_protocol.erl new file mode 100644 index 000000000..2fe10d6a2 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_protocol.erl @@ -0,0 +1,412 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_protocol). + +-export([new/2, + write/2, + read/2, + read/3, + skip/2, + flush_transport/1, + close_transport/1, + typeid_to_atom/1 + ]). + +-export([behaviour_info/1]). + +-include("thrift_constants.hrl"). +-include("thrift_protocol.hrl"). + +-record(protocol, {module, data}). + +behaviour_info(callbacks) -> + [ + {read, 2}, + {write, 2}, + {flush_transport, 1}, + {close_transport, 1} + ]; +behaviour_info(_Else) -> undefined. + +new(Module, Data) when is_atom(Module) -> + {ok, #protocol{module = Module, + data = Data}}. + +-spec flush_transport(#protocol{}) -> {#protocol{}, ok}. +flush_transport(Proto = #protocol{module = Module, + data = Data}) -> + {NewData, Result} = Module:flush_transport(Data), + {Proto#protocol{data = NewData}, Result}. + +-spec close_transport(#protocol{}) -> ok. +close_transport(#protocol{module = Module, + data = Data}) -> + Module:close_transport(Data). + +typeid_to_atom(?tType_STOP) -> field_stop; +typeid_to_atom(?tType_VOID) -> void; +typeid_to_atom(?tType_BOOL) -> bool; +typeid_to_atom(?tType_DOUBLE) -> double; +typeid_to_atom(?tType_I8) -> byte; +typeid_to_atom(?tType_I16) -> i16; +typeid_to_atom(?tType_I32) -> i32; +typeid_to_atom(?tType_I64) -> i64; +typeid_to_atom(?tType_STRING) -> string; +typeid_to_atom(?tType_STRUCT) -> struct; +typeid_to_atom(?tType_MAP) -> map; +typeid_to_atom(?tType_SET) -> set; +typeid_to_atom(?tType_LIST) -> list. + +term_to_typeid(void) -> ?tType_VOID; +term_to_typeid(bool) -> ?tType_BOOL; +term_to_typeid(byte) -> ?tType_I8; +term_to_typeid(double) -> ?tType_DOUBLE; +term_to_typeid(i8) -> ?tType_I8; +term_to_typeid(i16) -> ?tType_I16; +term_to_typeid(i32) -> ?tType_I32; +term_to_typeid(i64) -> ?tType_I64; +term_to_typeid(string) -> ?tType_STRING; +term_to_typeid({struct, _}) -> ?tType_STRUCT; +term_to_typeid({map, _, _}) -> ?tType_MAP; +term_to_typeid({set, _}) -> ?tType_SET; +term_to_typeid({list, _}) -> ?tType_LIST. + +%% Structure is like: +%% [{Fid, Type}, ...] +-spec read(#protocol{}, {struct, _StructDef}, atom()) -> {#protocol{}, {ok, tuple()}}. +read(IProto0, {struct, Structure}, Tag) + when is_list(Structure), is_atom(Tag) -> + + % If we want a tagged tuple, we need to offset all the tuple indices + % by 1 to avoid overwriting the tag. + Offset = if Tag =/= undefined -> 1; true -> 0 end, + IndexList = case length(Structure) of + N when N > 0 -> lists:seq(1 + Offset, N + Offset); + _ -> [] + end, + + SWithIndices = [{Fid, {Type, Index}} || + {{Fid, Type}, Index} <- + lists:zip(Structure, IndexList)], + % Fid -> {Type, Index} + SDict = dict:from_list(SWithIndices), + + {IProto1, ok} = read(IProto0, struct_begin), + RTuple0 = erlang:make_tuple(length(Structure) + Offset, undefined), + RTuple1 = if Tag =/= undefined -> setelement(1, RTuple0, Tag); + true -> RTuple0 + end, + + {IProto2, RTuple2} = read_struct_loop(IProto1, SDict, RTuple1), + {IProto2, {ok, RTuple2}}. + + +%% NOTE: Keep this in sync with thrift_protocol_behaviour:read +-spec read + (#protocol{}, {struct, _Info}) -> {#protocol{}, {ok, tuple()} | {error, _Reason}}; + (#protocol{}, tprot_cont_tag()) -> {#protocol{}, {ok, any()} | {error, _Reason}}; + (#protocol{}, tprot_empty_tag()) -> {#protocol{}, ok | {error, _Reason}}; + (#protocol{}, tprot_header_tag()) -> {#protocol{}, tprot_header_val() | {error, _Reason}}; + (#protocol{}, tprot_data_tag()) -> {#protocol{}, {ok, any()} | {error, _Reason}}. + +read(IProto, {struct, {Module, StructureName}}) when is_atom(Module), + is_atom(StructureName) -> + read(IProto, Module:struct_info(StructureName), StructureName); + +read(IProto, S={struct, Structure}) when is_list(Structure) -> + read(IProto, S, undefined); + +read(IProto0, {list, Type}) -> + {IProto1, #protocol_list_begin{etype = EType, size = Size}} = + read(IProto0, list_begin), + {EType, EType} = {term_to_typeid(Type), EType}, + {List, IProto2} = lists:mapfoldl(fun(_, ProtoS0) -> + {ProtoS1, {ok, Item}} = read(ProtoS0, Type), + {Item, ProtoS1} + end, + IProto1, + lists:duplicate(Size, 0)), + {IProto3, ok} = read(IProto2, list_end), + {IProto3, {ok, List}}; + +read(IProto0, {map, KeyType, ValType}) -> + {IProto1, #protocol_map_begin{size = Size, ktype = KType, vtype = VType}} = + read(IProto0, map_begin), + _ = case Size of + 0 -> 0; + _ -> + {KType, KType} = {term_to_typeid(KeyType), KType}, + {VType, VType} = {term_to_typeid(ValType), VType} + end, + {List, IProto2} = lists:mapfoldl(fun(_, ProtoS0) -> + {ProtoS1, {ok, Key}} = read(ProtoS0, KeyType), + {ProtoS2, {ok, Val}} = read(ProtoS1, ValType), + {{Key, Val}, ProtoS2} + end, + IProto1, + lists:duplicate(Size, 0)), + {IProto3, ok} = read(IProto2, map_end), + {IProto3, {ok, dict:from_list(List)}}; + +read(IProto0, {set, Type}) -> + {IProto1, #protocol_set_begin{etype = EType, size = Size}} = + read(IProto0, set_begin), + {EType, EType} = {term_to_typeid(Type), EType}, + {List, IProto2} = lists:mapfoldl(fun(_, ProtoS0) -> + {ProtoS1, {ok, Item}} = read(ProtoS0, Type), + {Item, ProtoS1} + end, + IProto1, + lists:duplicate(Size, 0)), + {IProto3, ok} = read(IProto2, set_end), + {IProto3, {ok, sets:from_list(List)}}; + +read(Protocol, ProtocolType) -> + read_specific(Protocol, ProtocolType). + +%% NOTE: Keep this in sync with thrift_protocol_behaviour:read +-spec read_specific + (#protocol{}, tprot_empty_tag()) -> {#protocol{}, ok | {error, _Reason}}; + (#protocol{}, tprot_header_tag()) -> {#protocol{}, tprot_header_val() | {error, _Reason}}; + (#protocol{}, tprot_data_tag()) -> {#protocol{}, {ok, any()} | {error, _Reason}}. +read_specific(Proto = #protocol{module = Module, + data = ModuleData}, ProtocolType) -> + {NewData, Result} = Module:read(ModuleData, ProtocolType), + {Proto#protocol{data = NewData}, Result}. + +read_struct_loop(IProto0, SDict, RTuple) -> + {IProto1, #protocol_field_begin{type = FType, id = Fid}} = + thrift_protocol:read(IProto0, field_begin), + case {FType, Fid} of + {?tType_STOP, _} -> + {IProto2, ok} = read(IProto1, struct_end), + {IProto2, RTuple}; + _Else -> + case dict:find(Fid, SDict) of + {ok, {Type, Index}} -> + case term_to_typeid(Type) of + FType -> + {IProto2, {ok, Val}} = read(IProto1, Type), + {IProto3, ok} = thrift_protocol:read(IProto2, field_end), + NewRTuple = setelement(Index, RTuple, Val), + read_struct_loop(IProto3, SDict, NewRTuple); + Expected -> + error_logger:info_msg( + "Skipping field ~p with wrong type (~p != ~p)~n", + [Fid, FType, Expected]), + skip_field(FType, IProto1, SDict, RTuple) + end; + _Else2 -> + skip_field(FType, IProto1, SDict, RTuple) + end + end. + +skip_field(FType, IProto0, SDict, RTuple) -> + {IProto1, ok} = skip(IProto0, typeid_to_atom(FType)), + {IProto2, ok} = read(IProto1, field_end), + read_struct_loop(IProto2, SDict, RTuple). + +-spec skip(#protocol{}, atom()) -> {#protocol{}, ok}. + +skip(Proto0, struct) -> + {Proto1, ok} = read(Proto0, struct_begin), + {Proto2, ok} = skip_struct_loop(Proto1), + {Proto3, ok} = read(Proto2, struct_end), + {Proto3, ok}; + +skip(Proto0, map) -> + {Proto1, Map} = read(Proto0, map_begin), + {Proto2, ok} = skip_map_loop(Proto1, Map), + {Proto3, ok} = read(Proto2, map_end), + {Proto3, ok}; + +skip(Proto0, set) -> + {Proto1, Set} = read(Proto0, set_begin), + {Proto2, ok} = skip_set_loop(Proto1, Set), + {Proto3, ok} = read(Proto2, set_end), + {Proto3, ok}; + +skip(Proto0, list) -> + {Proto1, List} = read(Proto0, list_begin), + {Proto2, ok} = skip_list_loop(Proto1, List), + {Proto3, ok} = read(Proto2, list_end), + {Proto3, ok}; + +skip(Proto0, Type) when is_atom(Type) -> + {Proto1, _Ignore} = read(Proto0, Type), + {Proto1, ok}. + + +skip_struct_loop(Proto0) -> + {Proto1, #protocol_field_begin{type = Type}} = read(Proto0, field_begin), + case Type of + ?tType_STOP -> + {Proto1, ok}; + _Else -> + {Proto2, ok} = skip(Proto1, typeid_to_atom(Type)), + {Proto3, ok} = read(Proto2, field_end), + skip_struct_loop(Proto3) + end. + +skip_map_loop(Proto0, Map = #protocol_map_begin{ktype = Ktype, + vtype = Vtype, + size = Size}) -> + case Size of + N when N > 0 -> + {Proto1, ok} = skip(Proto0, typeid_to_atom(Ktype)), + {Proto2, ok} = skip(Proto1, typeid_to_atom(Vtype)), + skip_map_loop(Proto2, + Map#protocol_map_begin{size = Size - 1}); + 0 -> {Proto0, ok} + end. + +skip_set_loop(Proto0, Map = #protocol_set_begin{etype = Etype, + size = Size}) -> + case Size of + N when N > 0 -> + {Proto1, ok} = skip(Proto0, typeid_to_atom(Etype)), + skip_set_loop(Proto1, + Map#protocol_set_begin{size = Size - 1}); + 0 -> {Proto0, ok} + end. + +skip_list_loop(Proto0, Map = #protocol_list_begin{etype = Etype, + size = Size}) -> + case Size of + N when N > 0 -> + {Proto1, ok} = skip(Proto0, typeid_to_atom(Etype)), + skip_list_loop(Proto1, + Map#protocol_list_begin{size = Size - 1}); + 0 -> {Proto0, ok} + end. + + +%%-------------------------------------------------------------------- +%% Function: write(OProto, {Type, Data}) -> ok +%% +%% Type = {struct, StructDef} | +%% {list, Type} | +%% {map, KeyType, ValType} | +%% {set, Type} | +%% BaseType +%% +%% Data = +%% tuple() -- for struct +%% | list() -- for list +%% | dictionary() -- for map +%% | set() -- for set +%% | any() -- for base types +%% +%% Description: +%%-------------------------------------------------------------------- +-spec write(#protocol{}, any()) -> {#protocol{}, ok | {error, _Reason}}. + +write(Proto0, {{struct, StructDef}, Data}) + when is_list(StructDef), is_tuple(Data), length(StructDef) == size(Data) - 1 -> + + [StructName | Elems] = tuple_to_list(Data), + {Proto1, ok} = write(Proto0, #protocol_struct_begin{name = StructName}), + {Proto2, ok} = struct_write_loop(Proto1, StructDef, Elems), + {Proto3, ok} = write(Proto2, struct_end), + {Proto3, ok}; + +write(Proto, {{struct, {Module, StructureName}}, Data}) + when is_atom(Module), + is_atom(StructureName), + element(1, Data) =:= StructureName -> + write(Proto, {Module:struct_info(StructureName), Data}); + +write(_, {{struct, {Module, StructureName}}, Data}) + when is_atom(Module), + is_atom(StructureName) -> + erlang:error(struct_unmatched, {{provided, element(1, Data)}, + {expected, StructureName}}); + +write(Proto0, {{list, Type}, Data}) + when is_list(Data) -> + {Proto1, ok} = write(Proto0, + #protocol_list_begin{ + etype = term_to_typeid(Type), + size = length(Data) + }), + Proto2 = lists:foldl(fun(Elem, ProtoIn) -> + {ProtoOut, ok} = write(ProtoIn, {Type, Elem}), + ProtoOut + end, + Proto1, + Data), + {Proto3, ok} = write(Proto2, list_end), + {Proto3, ok}; + +write(Proto0, {{map, KeyType, ValType}, Data}) -> + {Proto1, ok} = write(Proto0, + #protocol_map_begin{ + ktype = term_to_typeid(KeyType), + vtype = term_to_typeid(ValType), + size = dict:size(Data) + }), + Proto2 = dict:fold(fun(KeyData, ValData, ProtoS0) -> + {ProtoS1, ok} = write(ProtoS0, {KeyType, KeyData}), + {ProtoS2, ok} = write(ProtoS1, {ValType, ValData}), + ProtoS2 + end, + Proto1, + Data), + {Proto3, ok} = write(Proto2, map_end), + {Proto3, ok}; + +write(Proto0, {{set, Type}, Data}) -> + true = sets:is_set(Data), + {Proto1, ok} = write(Proto0, + #protocol_set_begin{ + etype = term_to_typeid(Type), + size = sets:size(Data) + }), + Proto2 = sets:fold(fun(Elem, ProtoIn) -> + {ProtoOut, ok} = write(ProtoIn, {Type, Elem}), + ProtoOut + end, + Proto1, + Data), + {Proto3, ok} = write(Proto2, set_end), + {Proto3, ok}; + +write(Proto = #protocol{module = Module, + data = ModuleData}, Data) -> + {NewData, Result} = Module:write(ModuleData, Data), + {Proto#protocol{data = NewData}, Result}. + +struct_write_loop(Proto0, [{Fid, Type} | RestStructDef], [Data | RestData]) -> + NewProto = case Data of + undefined -> + Proto0; % null fields are skipped in response + _ -> + {Proto1, ok} = write(Proto0, + #protocol_field_begin{ + type = term_to_typeid(Type), + id = Fid + }), + {Proto2, ok} = write(Proto1, {Type, Data}), + {Proto3, ok} = write(Proto2, field_end), + Proto3 + end, + struct_write_loop(NewProto, RestStructDef, RestData); +struct_write_loop(Proto, [], []) -> + write(Proto, field_stop). diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_reconnecting_client.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_reconnecting_client.erl new file mode 100644 index 000000000..538fd3ad1 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_reconnecting_client.erl @@ -0,0 +1,258 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_reconnecting_client). + +-behaviour(gen_server). + +%% API +-export([ call/3, + get_stats/1, + get_and_reset_stats/1 ]). + +-export([ start_link/6 ]). + +%% gen_server callbacks +-export([ init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3 ]). + +-record( state, { client = nil, + host, + port, + thrift_svc, + thrift_opts, + reconn_min, + reconn_max, + reconn_time = 0, + op_cnt_dict, + op_time_dict } ). + +%%==================================================================== +%% API +%%==================================================================== +%%-------------------------------------------------------------------- +%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} +%% Description: Starts the server +%%-------------------------------------------------------------------- +start_link( Host, Port, + ThriftSvc, ThriftOpts, + ReconnMin, ReconnMax ) -> + gen_server:start_link( ?MODULE, + [ Host, Port, + ThriftSvc, ThriftOpts, + ReconnMin, ReconnMax ], + [] ). + +call( Pid, Op, Args ) -> + gen_server:call( Pid, { call, Op, Args } ). + +get_stats( Pid ) -> + gen_server:call( Pid, get_stats ). + +get_and_reset_stats( Pid ) -> + gen_server:call( Pid, get_and_reset_stats ). + +%%==================================================================== +%% gen_server callbacks +%%==================================================================== + +%%-------------------------------------------------------------------- +%% Function: init(Args) -> {ok, State} | +%% {ok, State, Timeout} | +%% ignore | +%% {stop, Reason} +%% Description: Start the server. +%%-------------------------------------------------------------------- +init( [ Host, Port, TSvc, TOpts, ReconnMin, ReconnMax ] ) -> + process_flag( trap_exit, true ), + + State = #state{ host = Host, + port = Port, + thrift_svc = TSvc, + thrift_opts = TOpts, + reconn_min = ReconnMin, + reconn_max = ReconnMax, + op_cnt_dict = dict:new(), + op_time_dict = dict:new() }, + + { ok, try_connect( State ) }. + +%%-------------------------------------------------------------------- +%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | +%% {reply, Reply, State, Timeout} | +%% {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, Reply, State} | +%% {stop, Reason, State} +%% Description: Handling call messages +%%-------------------------------------------------------------------- +handle_call( { call, Op, _ }, + _From, + State = #state{ client = nil } ) -> + { reply, { error, noconn }, incr_stats( Op, "failfast", 1, State ) }; + +handle_call( { call, Op, Args }, + _From, + State=#state{ client = Client } ) -> + + Timer = timer_fun(), + Result = ( catch thrift_client:call( Client, Op, Args) ), + Time = Timer(), + + case Result of + { C, { ok, Reply } } -> + S = incr_stats( Op, "success", Time, State#state{ client = C } ), + { reply, {ok, Reply }, S }; + { _, { E, Msg } } when E == error; E == exception -> + S = incr_stats( Op, "error", Time, try_connect( State ) ), + { reply, { E, Msg }, S }; + Other -> + S = incr_stats( Op, "error", Time, try_connect( State ) ), + { reply, Other, S } + end; + +handle_call( get_stats, + _From, + State = #state{} ) -> + { reply, stats( State ), State }; + +handle_call( get_and_reset_stats, + _From, + State = #state{} ) -> + { reply, stats( State ), reset_stats( State ) }. + +%%-------------------------------------------------------------------- +%% Function: handle_cast(Msg, State) -> {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, State} +%% Description: Handling cast messages +%%-------------------------------------------------------------------- +handle_cast( _Msg, State ) -> + { noreply, State }. + +%%-------------------------------------------------------------------- +%% Function: handle_info(Info, State) -> {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, State} +%% Description: Handling all non call/cast messages +%%-------------------------------------------------------------------- +handle_info( try_connect, State ) -> + { noreply, try_connect( State ) }; + +handle_info( _Info, State ) -> + { noreply, State }. + +%%-------------------------------------------------------------------- +%% Function: terminate(Reason, State) -> void() +%% Description: This function is called by a gen_server when it is about to +%% terminate. It should be the opposite of Module:init/1 and do any necessary +%% cleaning up. When it returns, the gen_server terminates with Reason. +%% The return value is ignored. +%%-------------------------------------------------------------------- +terminate( _Reason, #state{ client = Client } ) -> + thrift_client:close( Client ), + ok. + +%%-------------------------------------------------------------------- +%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} +%% Description: Convert process state when code is changed +%%-------------------------------------------------------------------- +code_change( _OldVsn, State, _Extra ) -> + { ok, State }. + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +try_connect( State = #state{ client = OldClient, + host = Host, + port = Port, + thrift_svc = TSvc, + thrift_opts = TOpts } ) -> + + case OldClient of + nil -> ok; + _ -> ( catch thrift_client:close( OldClient ) ) + end, + + case catch thrift_client_util:new( Host, Port, TSvc, TOpts ) of + { ok, Client } -> + State#state{ client = Client, reconn_time = 0 }; + { E, Msg } when E == error; E == exception -> + ReconnTime = reconn_time( State ), + error_logger:error_msg( "[~w] ~w connect failed (~w), trying again in ~w ms~n", + [ self(), TSvc, Msg, ReconnTime ] ), + erlang:send_after( ReconnTime, self(), try_connect ), + State#state{ client = nil, reconn_time = ReconnTime } + end. + + +reconn_time( #state{ reconn_min = ReconnMin, reconn_time = 0 } ) -> + ReconnMin; +reconn_time( #state{ reconn_max = ReconnMax, reconn_time = ReconnMax } ) -> + ReconnMax; +reconn_time( #state{ reconn_max = ReconnMax, reconn_time = R } ) -> + Backoff = 2 * R, + case Backoff > ReconnMax of + true -> ReconnMax; + false -> Backoff + end. + +-ifdef(time_correction). +timer_fun() -> + T1 = erlang:monotonic_time(), + fun() -> + T2 = erlang:monotonic_time(), + erlang:convert_time_unit(T2 - T1, native, micro_seconds) + end. +-else. +timer_fun() -> + T1 = erlang:timestamp(), + fun() -> + T2 = erlang:timestamp(), + timer:now_diff(T2, T1) + end. +-endif. + +incr_stats( Op, Result, Time, + State = #state{ op_cnt_dict = OpCntDict, + op_time_dict = OpTimeDict } ) -> + Key = lists:flatten( [ atom_to_list( Op ), [ "_" | Result ] ] ), + State#state{ op_cnt_dict = dict:update_counter( Key, 1, OpCntDict ), + op_time_dict = dict:update_counter( Key, Time, OpTimeDict ) }. + + +stats( #state{ thrift_svc = TSvc, + op_cnt_dict = OpCntDict, + op_time_dict = OpTimeDict } ) -> + Svc = atom_to_list(TSvc), + + F = fun( Key, Count, Stats ) -> + Name = lists:flatten( [ Svc, [ "_" | Key ] ] ), + Micros = dict:fetch( Key, OpTimeDict ), + [ { Name, Count, Micros } | Stats ] + end, + + dict:fold( F, [], OpCntDict ). + +reset_stats( State = #state{} ) -> + State#state{ op_cnt_dict = dict:new(), op_time_dict = dict:new() }. diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_server.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_server.erl new file mode 100644 index 000000000..5012e168f --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_server.erl @@ -0,0 +1,183 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_server). + +-behaviour(gen_server). + +%% API +-export([start_link/3, stop/1, take_socket/2]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +-define(SERVER, ?MODULE). + +-record(state, {listen_socket, acceptor_ref, service, handler}). + +%%==================================================================== +%% API +%%==================================================================== +%%-------------------------------------------------------------------- +%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} +%% Description: Starts the server +%%-------------------------------------------------------------------- +start_link(Port, Service, HandlerModule) when is_integer(Port), is_atom(HandlerModule) -> + gen_server:start_link({local, ?SERVER}, ?MODULE, {Port, Service, HandlerModule}, []). + +%%-------------------------------------------------------------------- +%% Function: stop(Pid) -> ok, {error, Reason} +%% Description: Stops the server. +%%-------------------------------------------------------------------- +stop(Pid) when is_pid(Pid) -> + gen_server:call(Pid, stop). + + +take_socket(Server, Socket) -> + gen_server:call(Server, {take_socket, Socket}). + + +%%==================================================================== +%% gen_server callbacks +%%==================================================================== + +%%-------------------------------------------------------------------- +%% Function: init(Args) -> {ok, State} | +%% {ok, State, Timeout} | +%% ignore | +%% {stop, Reason} +%% Description: Initiates the server +%%-------------------------------------------------------------------- +init({Port, Service, Handler}) -> + {ok, Socket} = gen_tcp:listen(Port, + [binary, + {packet, 0}, + {active, false}, + {nodelay, true}, + {reuseaddr, true}]), + {ok, Ref} = prim_inet:async_accept(Socket, -1), + {ok, #state{listen_socket = Socket, + acceptor_ref = Ref, + service = Service, + handler = Handler}}. + +%%-------------------------------------------------------------------- +%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | +%% {reply, Reply, State, Timeout} | +%% {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, Reply, State} | +%% {stop, Reason, State} +%% Description: Handling call messages +%%-------------------------------------------------------------------- +handle_call(stop, _From, State) -> + {stop, stopped, ok, State}; + +handle_call({take_socket, Socket}, {FromPid, _Tag}, State) -> + Result = gen_tcp:controlling_process(Socket, FromPid), + {reply, Result, State}. + +%%-------------------------------------------------------------------- +%% Function: handle_cast(Msg, State) -> {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, State} +%% Description: Handling cast messages +%%-------------------------------------------------------------------- +handle_cast(_Msg, State) -> + {noreply, State}. + +%%-------------------------------------------------------------------- +%% Function: handle_info(Info, State) -> {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, State} +%% Description: Handling all non call/cast messages +%%-------------------------------------------------------------------- +handle_info({inet_async, ListenSocket, Ref, {ok, ClientSocket}}, + State = #state{listen_socket = ListenSocket, + acceptor_ref = Ref, + service = Service, + handler = Handler}) -> + case set_sockopt(ListenSocket, ClientSocket) of + ok -> + %% New client connected - start processor + start_processor(ClientSocket, Service, Handler), + {ok, NewRef} = prim_inet:async_accept(ListenSocket, -1), + {noreply, State#state{acceptor_ref = NewRef}}; + {error, Reason} -> + error_logger:error_msg("Couldn't set socket opts: ~p~n", + [Reason]), + {stop, Reason, State} + end; + +handle_info({inet_async, _ListenSocket, _Ref, Error}, State) -> + error_logger:error_msg("Error in acceptor: ~p~n", [Error]), + {stop, Error, State}; + +handle_info(_Info, State) -> + {noreply, State}. + +%%-------------------------------------------------------------------- +%% Function: terminate(Reason, State) -> void() +%% Description: This function is called by a gen_server when it is about to +%% terminate. It should be the opposite of Module:init/1 and do any necessary +%% cleaning up. When it returns, the gen_server terminates with Reason. +%% The return value is ignored. +%%-------------------------------------------------------------------- +terminate(_Reason, _State) -> + ok. + +%%-------------------------------------------------------------------- +%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} +%% Description: Convert process state when code is changed +%%-------------------------------------------------------------------- +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +set_sockopt(ListenSocket, ClientSocket) -> + true = inet_db:register_socket(ClientSocket, inet_tcp), + case prim_inet:getopts(ListenSocket, + [active, nodelay, keepalive, delay_send, priority, tos]) of + {ok, Opts} -> + case prim_inet:setopts(ClientSocket, Opts) of + ok -> ok; + Error -> gen_tcp:close(ClientSocket), + Error + end; + Error -> + gen_tcp:close(ClientSocket), + Error + end. + +start_processor(Socket, Service, Handler) -> + Server = self(), + + ProtoGen = fun() -> + % Become the controlling process + ok = take_socket(Server, Socket), + {ok, SocketTransport} = thrift_socket_transport:new(Socket), + {ok, BufferedTransport} = thrift_buffered_transport:new(SocketTransport), + {ok, Protocol} = thrift_binary_protocol:new(BufferedTransport), + {ok, Protocol} + end, + + spawn(thrift_processor, init, [{Server, ProtoGen, Service, Handler}]). diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_service.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_service.erl new file mode 100644 index 000000000..2ed7b57b0 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_service.erl @@ -0,0 +1,25 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_service). + +-export([behaviour_info/1]). + +behaviour_info(callbacks) -> + [{function_info, 2}]. diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_socket_server.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_socket_server.erl new file mode 100644 index 000000000..432e65b56 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_socket_server.erl @@ -0,0 +1,324 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_socket_server). + +-behaviour(gen_server). + +-include ("thrift_constants.hrl"). + +-ifdef(TEST). + -compile(export_all). + -export_records([thrift_socket_server]). +-else. + -export([start/1, stop/1]). + + -export([init/1, handle_call/3, handle_cast/2, terminate/2, code_change/3, + handle_info/2]). + + -export([acceptor_loop/1]). +-endif. + +-record(thrift_socket_server, + {port, + service, + handler, + name, + max=2048, + ip=any, + listen=null, + acceptor=null, + socket_opts=[{recv_timeout, 500}], + protocol=binary, + framed=false, + ssltransport=false, + ssloptions=[] + }). + +start(State=#thrift_socket_server{}) -> + start_server(State); +start(Options) -> + start(parse_options(Options)). + +stop(Name) when is_atom(Name) -> + gen_server:cast(Name, stop); +stop(Pid) when is_pid(Pid) -> + gen_server:cast(Pid, stop); +stop({local, Name}) -> + stop(Name); +stop({global, Name}) -> + stop(Name); +stop(Options) -> + State = parse_options(Options), + stop(State#thrift_socket_server.name). + +%% Internal API + +parse_options(Options) -> + parse_options(Options, #thrift_socket_server{}). + +parse_options([], State) -> + State; +parse_options([{name, L} | Rest], State) when is_list(L) -> + Name = {local, list_to_atom(L)}, + parse_options(Rest, State#thrift_socket_server{name=Name}); +parse_options([{name, A} | Rest], State) when is_atom(A) -> + Name = {local, A}, + parse_options(Rest, State#thrift_socket_server{name=Name}); +parse_options([{name, Name} | Rest], State) -> + parse_options(Rest, State#thrift_socket_server{name=Name}); +parse_options([{port, L} | Rest], State) when is_list(L) -> + Port = list_to_integer(L), + parse_options(Rest, State#thrift_socket_server{port=Port}); +parse_options([{port, Port} | Rest], State) -> + parse_options(Rest, State#thrift_socket_server{port=Port}); +parse_options([{ip, Ip} | Rest], State) -> + ParsedIp = case Ip of + any -> + any; + Ip when is_tuple(Ip) -> + Ip; + Ip when is_list(Ip) -> + {ok, IpTuple} = inet_parse:address(Ip), + IpTuple + end, + parse_options(Rest, State#thrift_socket_server{ip=ParsedIp}); +parse_options([{socket_opts, L} | Rest], State) when is_list(L), length(L) > 0 -> + parse_options(Rest, State#thrift_socket_server{socket_opts=L}); + +parse_options([{handler, []} | _Rest], _State) -> + throw("At least an error handler must be defined."); +parse_options([{handler, ServiceHandlerPropertyList} | Rest], State) when is_list(ServiceHandlerPropertyList) -> + ServiceHandlerMap = + case State#thrift_socket_server.handler of + undefined -> + lists:foldl( + fun ({ServiceName, ServiceHandler}, Acc) when is_list(ServiceName), is_atom(ServiceHandler) -> + thrift_multiplexed_map_wrapper:store(ServiceName, ServiceHandler, Acc); + (_, _Acc) -> + throw("The handler option is not properly configured for multiplexed services. It should be a kind of [{\"error_handler\", Module::atom()}, {SericeName::list(), Module::atom()}, ...]") + end, thrift_multiplexed_map_wrapper:new(), ServiceHandlerPropertyList); + _ -> throw("Error while parsing the handler option.") + end, + case thrift_multiplexed_map_wrapper:find(?MULTIPLEXED_ERROR_HANDLER_KEY, ServiceHandlerMap) of + {ok, _ErrorHandler} -> parse_options(Rest, State#thrift_socket_server{handler=ServiceHandlerMap}); + error -> throw("The handler option is not properly configured for multiplexed services. It should be a kind of [{\"error_handler\", Module::atom()}, {SericeName::list(), Module::atom()}, ...]") + end; +parse_options([{handler, Handler} | Rest], State) when State#thrift_socket_server.handler == undefined, is_atom(Handler) -> + parse_options(Rest, State#thrift_socket_server{handler=Handler}); + +parse_options([{service, []} | _Rest], _State) -> + throw("At least one service module must be defined."); +parse_options([{service, ServiceModulePropertyList} | Rest], State) when is_list(ServiceModulePropertyList) -> + ServiceModuleMap = + case State#thrift_socket_server.service of + undefined -> + lists:foldl( + fun ({ServiceName, ServiceModule}, Acc) when is_list(ServiceName), is_atom(ServiceModule) -> + thrift_multiplexed_map_wrapper:store(ServiceName, ServiceModule, Acc); + (_, _Acc) -> + throw("The service option is not properly configured for multiplexed services. It should be a kind of [{SericeName::list(), ServiceModule::atom()}, ...]") + end, thrift_multiplexed_map_wrapper:new(), ServiceModulePropertyList); + _ -> throw("Error while parsing the service option.") + end, + parse_options(Rest, State#thrift_socket_server{service=ServiceModuleMap}); +parse_options([{service, Service} | Rest], State) when State#thrift_socket_server.service == undefined, is_atom(Service) -> + parse_options(Rest, State#thrift_socket_server{service=Service}); + +parse_options([{max, Max} | Rest], State) -> + MaxInt = case Max of + Max when is_list(Max) -> + list_to_integer(Max); + Max when is_integer(Max) -> + Max + end, + parse_options(Rest, State#thrift_socket_server{max=MaxInt}); + +parse_options([{protocol, Proto} | Rest], State) when is_atom(Proto) -> + parse_options(Rest, State#thrift_socket_server{protocol=Proto}); + +parse_options([{framed, Framed} | Rest], State) when is_boolean(Framed) -> + parse_options(Rest, State#thrift_socket_server{framed=Framed}); + +parse_options([{ssltransport, SSLTransport} | Rest], State) when is_boolean(SSLTransport) -> + parse_options(Rest, State#thrift_socket_server{ssltransport=SSLTransport}); +parse_options([{ssloptions, SSLOptions} | Rest], State) when is_list(SSLOptions) -> + parse_options(Rest, State#thrift_socket_server{ssloptions=SSLOptions}). + +start_server(State=#thrift_socket_server{name=Name}) -> + case Name of + undefined -> + gen_server:start_link(?MODULE, State, []); + _ -> + gen_server:start_link(Name, ?MODULE, State, []) + end. + +init(State=#thrift_socket_server{ip=Ip, port=Port}) -> + process_flag(trap_exit, true), + BaseOpts = [binary, + {reuseaddr, true}, + {packet, 0}, + {backlog, 4096}, + {recbuf, 8192}, + {active, false}], + Opts = case Ip of + any -> + BaseOpts; + Ip -> + [{ip, Ip} | BaseOpts] + end, + case gen_tcp_listen(Port, Opts, State) of + {stop, eacces} -> + %% fdsrv module allows another shot to bind + %% ports which require root access + case Port < 1024 of + true -> + case fdsrv:start() of + {ok, _} -> + case fdsrv:bind_socket(tcp, Port) of + {ok, Fd} -> + gen_tcp_listen(Port, [{fd, Fd} | Opts], State); + _ -> + {stop, fdsrv_bind_failed} + end; + _ -> + {stop, fdsrv_start_failed} + end; + false -> + {stop, eacces} + end; + Other -> + error_logger:info_msg("thrift service listening on port ~p", [Port]), + Other + end. + +gen_tcp_listen(Port, Opts, State) -> + case gen_tcp:listen(Port, Opts) of + {ok, Listen} -> + {ok, ListenPort} = inet:port(Listen), + {ok, new_acceptor(State#thrift_socket_server{listen=Listen, + port=ListenPort})}; + {error, Reason} -> + {stop, Reason} + end. + +new_acceptor(State=#thrift_socket_server{max=0}) -> + error_logger:error_msg("Not accepting new connections"), + State#thrift_socket_server{acceptor=null}; +new_acceptor(State=#thrift_socket_server{listen=Listen, + service=Service, handler=Handler, + socket_opts=Opts, framed=Framed, protocol=Proto, + ssltransport=SslTransport, ssloptions=SslOptions + }) -> + Pid = proc_lib:spawn_link(?MODULE, acceptor_loop, + [{self(), Listen, Service, Handler, Opts, Framed, SslTransport, SslOptions, Proto}]), + State#thrift_socket_server{acceptor=Pid}. + +acceptor_loop({Server, Listen, Service, Handler, SocketOpts, Framed, SslTransport, SslOptions, Proto}) + when is_pid(Server), is_list(SocketOpts) -> + case catch gen_tcp:accept(Listen) of % infinite timeout + {ok, Socket} -> + gen_server:cast(Server, {accepted, self()}), + ProtoGen = fun() -> + {ok, SocketTransport} = case SslTransport of + true -> thrift_sslsocket_transport:new(Socket, SocketOpts, SslOptions); + false -> thrift_socket_transport:new(Socket, SocketOpts) + end, + {ok, Transport} = case Framed of + true -> thrift_framed_transport:new(SocketTransport); + false -> thrift_buffered_transport:new(SocketTransport) + end, + {ok, Protocol} = case Proto of + compact -> thrift_compact_protocol:new(Transport); + json -> thrift_json_protocol:new(Transport); + _ -> thrift_binary_protocol:new(Transport) + end, + {ok, Protocol} + end, + thrift_processor:init({Server, ProtoGen, Service, Handler}); + {error, closed} -> + exit({error, closed}); + Other -> + error_logger:error_report( + [{application, thrift}, + "Accept failed error", + lists:flatten(io_lib:format("~p", [Other]))]), + exit({error, accept_failed}) + end. + +handle_call({get, port}, _From, State=#thrift_socket_server{port=Port}) -> + {reply, Port, State}; +handle_call(_Message, _From, State) -> + Res = error, + {reply, Res, State}. + +handle_cast({accepted, Pid}, + State=#thrift_socket_server{acceptor=Pid, max=Max}) -> + % io:format("accepted ~p~n", [Pid]), + State1 = State#thrift_socket_server{max=Max - 1}, + {noreply, new_acceptor(State1)}; +handle_cast(stop, State) -> + {stop, normal, State}. + +terminate(Reason, #thrift_socket_server{listen=Listen, port=Port}) -> + gen_tcp:close(Listen), + case Reason of + normal -> ok; + shutdown -> ok; + _ -> {backtrace, Bt} = erlang:process_info(self(), backtrace), + error_logger:error_report({?MODULE, ?LINE, + {child_error, Reason, Bt}}) + end, + case Port < 1024 of + true -> + catch fdsrv:stop(), + ok; + false -> + ok + end. + +code_change(_OldVsn, State, _Extra) -> + State. + +handle_info({'EXIT', Pid, normal}, + State=#thrift_socket_server{acceptor=Pid}) -> + {noreply, new_acceptor(State)}; +handle_info({'EXIT', Pid, Reason}, + State=#thrift_socket_server{acceptor=Pid}) -> + error_logger:error_report({?MODULE, ?LINE, + {acceptor_error, Reason}}), + timer:sleep(100), + {noreply, new_acceptor(State)}; +handle_info({'EXIT', _LoopPid, Reason}, + State=#thrift_socket_server{acceptor=Pid, max=Max}) -> + case Reason of + normal -> ok; + shutdown -> ok; + _ -> error_logger:error_report({?MODULE, ?LINE, + {child_error, Reason, erlang:get_stacktrace()}}) + end, + State1 = State#thrift_socket_server{max=Max + 1}, + State2 = case Pid of + null -> new_acceptor(State1); + _ -> State1 + end, + {noreply, State2}; +handle_info(Info, State) -> + error_logger:info_report([{'INFO', Info}, {'State', State}]), + {noreply, State}. diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_socket_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_socket_transport.erl new file mode 100644 index 000000000..fa10ed0c6 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_socket_transport.erl @@ -0,0 +1,176 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_socket_transport). + +-behaviour(thrift_transport). + +%% constructors +-export([new/1, new/2]). +%% transport callbacks +-export([read/2, read_exact/2, write/2, flush/1, close/1]). +%% legacy api +-export([new_transport_factory/3]). + + +-record(t_socket, { + socket, + recv_timeout=60000, + buffer = [] +}). + +-type state() :: #t_socket{}. + + +-spec new(Socket::any()) -> + thrift_transport:t_transport(). + +new(Socket) -> new(Socket, []). + +-spec new(Socket::any(), Opts::list()) -> + thrift_transport:t_transport(). + +new(Socket, Opts) when is_list(Opts) -> + State = parse_opts(Opts, #t_socket{socket = Socket}), + thrift_transport:new(?MODULE, State). + + +parse_opts([{recv_timeout, Timeout}|Rest], State) +when is_integer(Timeout), Timeout > 0 -> + parse_opts(Rest, State#t_socket{recv_timeout = Timeout}); +parse_opts([{recv_timeout, infinity}|Rest], State) -> + parse_opts(Rest, State#t_socket{recv_timeout = infinity}); +parse_opts([], State) -> + State. + + +-include("thrift_transport_behaviour.hrl"). + + +read(State = #t_socket{buffer = Buf}, Len) +when is_integer(Len), Len >= 0 -> + Binary = iolist_to_binary(Buf), + case iolist_size(Binary) of + X when X >= Len -> + {Result, Remaining} = split_binary(Binary, Len), + {State#t_socket{buffer = Remaining}, {ok, Result}}; + _ -> recv(State, Len) + end. + +recv(State = #t_socket{socket = Socket, buffer = Buf}, Len) -> + case gen_tcp:recv(Socket, 0, State#t_socket.recv_timeout) of + {error, Error} -> + gen_tcp:close(Socket), + {State, {error, Error}}; + {ok, Data} -> + Binary = iolist_to_binary([Buf, Data]), + Give = min(iolist_size(Binary), Len), + {Result, Remaining} = split_binary(Binary, Give), + {State#t_socket{buffer = Remaining}, {ok, Result}} + end. + + +read_exact(State = #t_socket{buffer = Buf}, Len) +when is_integer(Len), Len >= 0 -> + Binary = iolist_to_binary(Buf), + case iolist_size(Binary) of + X when X >= Len -> read(State, Len); + X -> + case gen_tcp:recv(State#t_socket.socket, Len - X, State#t_socket.recv_timeout) of + {error, Error} -> + gen_tcp:close(State#t_socket.socket), + {State, {error, Error}}; + {ok, Data} -> + {State#t_socket{buffer = []}, {ok, <>}} + end + end. + + +write(State = #t_socket{socket = Socket}, Data) -> + case gen_tcp:send(Socket, Data) of + {error, Error} -> + gen_tcp:close(Socket), + {State, {error, Error}}; + ok -> {State, ok} + end. + + +flush(State) -> + {State#t_socket{buffer = []}, ok}. + + +close(State = #t_socket{socket = Socket}) -> + {State, gen_tcp:close(Socket)}. + + +%% legacy api. left for compatibility + +%% The following "local" record is filled in by parse_factory_options/2 +%% below. These options can be passed to new_protocol_factory/3 in a +%% proplists-style option list. They're parsed like this so it is an O(n) +%% operation instead of O(n^2) +-record(factory_opts, { + connect_timeout = infinity, + sockopts = [], + framed = false +}). + +parse_factory_options([], FactoryOpts, TransOpts) -> {FactoryOpts, TransOpts}; +parse_factory_options([{framed, Bool}|Rest], FactoryOpts, TransOpts) +when is_boolean(Bool) -> + parse_factory_options(Rest, FactoryOpts#factory_opts{framed = Bool}, TransOpts); +parse_factory_options([{sockopts, OptList}|Rest], FactoryOpts, TransOpts) +when is_list(OptList) -> + parse_factory_options(Rest, FactoryOpts#factory_opts{sockopts = OptList}, TransOpts); +parse_factory_options([{connect_timeout, TO}|Rest], FactoryOpts, TransOpts) +when TO =:= infinity; is_integer(TO) -> + parse_factory_options(Rest, FactoryOpts#factory_opts{connect_timeout = TO}, TransOpts); +parse_factory_options([{recv_timeout, TO}|Rest], FactoryOpts, TransOpts) +when TO =:= infinity; is_integer(TO) -> + parse_factory_options(Rest, FactoryOpts, [{recv_timeout, TO}] ++ TransOpts). + + +%% Generates a "transport factory" function - a fun which returns a thrift_transport() +%% instance. +%% State can be passed into a protocol factory to generate a connection to a +%% thrift server over a socket. +new_transport_factory(Host, Port, Options) -> + {FactoryOpts, TransOpts} = parse_factory_options(Options, #factory_opts{}, []), + {ok, fun() -> SockOpts = [binary, + {packet, 0}, + {active, false}, + {nodelay, true}|FactoryOpts#factory_opts.sockopts + ], + case catch gen_tcp:connect( + Host, + Port, + SockOpts, + FactoryOpts#factory_opts.connect_timeout + ) of + {ok, Sock} -> + {ok, Transport} = thrift_socket_transport:new(Sock, TransOpts), + {ok, BufTransport} = case FactoryOpts#factory_opts.framed of + true -> thrift_framed_transport:new(Transport); + false -> thrift_buffered_transport:new(Transport) + end, + {ok, BufTransport}; + Error -> Error + end + end}. + diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_sslsocket_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_sslsocket_transport.erl new file mode 100644 index 000000000..211153f7b --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_sslsocket_transport.erl @@ -0,0 +1,147 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +-module(thrift_sslsocket_transport). + +-include("thrift_transport_behaviour.hrl"). + +-behaviour(thrift_transport). + +-export([new/3, + write/2, read/2, flush/1, close/1, + + new_transport_factory/3]). + +%% Export only for the transport factory +-export([new/2]). + +-record(data, {socket, + recv_timeout=infinity}). +-type state() :: #data{}. + +%% The following "local" record is filled in by parse_factory_options/2 +%% below. These options can be passed to new_protocol_factory/3 in a +%% proplists-style option list. They're parsed like this so it is an O(n) +%% operation instead of O(n^2) +-record(factory_opts, {connect_timeout = infinity, + sockopts = [], + framed = false, + ssloptions = []}). + +parse_factory_options([], Opts) -> + Opts; +parse_factory_options([{framed, Bool} | Rest], Opts) when is_boolean(Bool) -> + parse_factory_options(Rest, Opts#factory_opts{framed=Bool}); +parse_factory_options([{sockopts, OptList} | Rest], Opts) when is_list(OptList) -> + parse_factory_options(Rest, Opts#factory_opts{sockopts=OptList}); +parse_factory_options([{connect_timeout, TO} | Rest], Opts) when TO =:= infinity; is_integer(TO) -> + parse_factory_options(Rest, Opts#factory_opts{connect_timeout=TO}); +parse_factory_options([{ssloptions, SslOptions} | Rest], Opts) when is_list(SslOptions) -> + parse_factory_options(Rest, Opts#factory_opts{ssloptions=SslOptions}). + +new(Socket, SockOpts, SslOptions) when is_list(SockOpts), is_list(SslOptions) -> + inet:setopts(Socket, [{active, false}]), %% => prevent the ssl handshake messages get lost + + %% upgrade to an ssl socket + case catch ssl:ssl_accept(Socket, SslOptions) of % infinite timeout + {ok, SslSocket} -> + new(SslSocket, SockOpts); + {error, Reason} -> + exit({error, Reason}); + Other -> + error_logger:error_report( + [{application, thrift}, + "SSL accept failed error", + lists:flatten(io_lib:format("~p", [Other]))]), + exit({error, ssl_accept_failed}) + end. + +new(SslSocket, SockOpts) -> + State = + case lists:keysearch(recv_timeout, 1, SockOpts) of + {value, {recv_timeout, Timeout}} + when is_integer(Timeout), Timeout > 0 -> + #data{socket=SslSocket, recv_timeout=Timeout}; + _ -> + #data{socket=SslSocket} + end, + thrift_transport:new(?MODULE, State). + +%% Data :: iolist() +write(This = #data{socket = Socket}, Data) -> + {This, ssl:send(Socket, Data)}. + +read(This = #data{socket=Socket, recv_timeout=Timeout}, Len) + when is_integer(Len), Len >= 0 -> + case ssl:recv(Socket, Len, Timeout) of + Err = {error, timeout} -> + error_logger:info_msg("read timeout: peer conn ~p", [inet:peername(Socket)]), + ssl:close(Socket), + {This, Err}; + Data -> + {This, Data} + end. + +%% We can't really flush - everything is flushed when we write +flush(This) -> + {This, ok}. + +close(This = #data{socket = Socket}) -> + {This, ssl:close(Socket)}. + +%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% +%% Generates a "transport factory" function - a fun which returns a thrift_transport() +%% instance. +%% This can be passed into a protocol factory to generate a connection to a +%% thrift server over a socket. +%% +new_transport_factory(Host, Port, Options) -> + ParsedOpts = parse_factory_options(Options, #factory_opts{}), + + F = fun() -> + SockOpts = [binary, + {packet, 0}, + {active, false}, + {nodelay, true} | + ParsedOpts#factory_opts.sockopts], + case catch gen_tcp:connect(Host, Port, SockOpts, + ParsedOpts#factory_opts.connect_timeout) of + {ok, Sock} -> + SslSock = case catch ssl:connect(Sock, ParsedOpts#factory_opts.ssloptions, + ParsedOpts#factory_opts.connect_timeout) of + {ok, SslSocket} -> + SslSocket; + Other -> + error_logger:info_msg("error while connecting over ssl - reason: ~p~n", [Other]), + catch gen_tcp:close(Sock), + exit(error) + end, + {ok, Transport} = thrift_sslsocket_transport:new(SslSock, SockOpts), + {ok, BufTransport} = + case ParsedOpts#factory_opts.framed of + true -> thrift_framed_transport:new(Transport); + false -> thrift_buffered_transport:new(Transport) + end, + {ok, BufTransport}; + Error -> + Error + end + end, + {ok, F}. \ No newline at end of file diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_transport.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_transport.erl new file mode 100644 index 000000000..2414bde36 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_transport.erl @@ -0,0 +1,126 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_transport). + +-export([behaviour_info/1]). +%% constructors +-export([new/1, new/2]). +%% transport callbacks +-export([read/2, read_exact/2, write/2, flush/1, close/1]). + +-export_type([t_transport/0]). + + +behaviour_info(callbacks) -> + [{read, 2}, {write, 2}, {flush, 1}, {close, 1}]. + + +-record(t_transport, { + module, + state +}). + +-type state() :: #t_transport{}. +-type t_transport() :: #t_transport{}. + + +-ifdef(transport_wrapper_module). +-define(debug_wrap(Transport), + case Transport#t_transport.module of + ?transport_wrapper_module -> Transport; + _Else -> + {ok, Result} = ?transport_wrapper_module:new(Transport), + Result + end +). +-else. +-define(debug_wrap(Transport), Transport). +-endif. + + +-type wrappable() :: + binary() | + list() | + {membuffer, binary() | list()} | + {tcp, port()} | + {tcp, port(), list()} | + {file, file:io_device()} | + {file, file:io_device(), list()} | + {file, file:filename()} | + {file, file:filename(), list()}. + +-spec new(wrappable()) -> {ok, #t_transport{}}. + +new({membuffer, Membuffer}) when is_binary(Membuffer); is_list(Membuffer) -> + thrift_membuffer_transport:new(Membuffer); +new({membuffer, Membuffer, []}) when is_binary(Membuffer); is_list(Membuffer) -> + thrift_membuffer_transport:new(Membuffer); +new({tcp, Socket}) when is_port(Socket) -> + new({tcp, Socket, []}); +new({tcp, Socket, Opts}) when is_port(Socket) -> + thrift_socket_transport:new(Socket, Opts); +new({file, Filename}) when is_list(Filename); is_binary(Filename) -> + new({file, Filename, []}); +new({file, Filename, Opts}) when is_list(Filename); is_binary(Filename) -> + {ok, File} = file:open(Filename, [raw, binary]), + new({file, File, Opts}); +new({file, File, Opts}) -> + thrift_file_transport:new(File, Opts). + +-spec new(Module::module(), State::any()) -> {ok, #t_transport{}}. + +new(Module, State) when is_atom(Module) -> + {ok, ?debug_wrap(#t_transport{module = Module, state = State})}. + + +-include("thrift_transport_behaviour.hrl"). + + +read(Transport = #t_transport{module = Module}, Len) +when is_integer(Len), Len >= 0 -> + {NewState, Result} = Module:read(Transport#t_transport.state, Len), + {Transport#t_transport{state = NewState}, Result}. + + +read_exact(Transport = #t_transport{module = Module}, Len) +when is_integer(Len), Len >= 0 -> + case lists:keyfind(read_exact, 1, Module:module_info(exports)) of + {read_exact, 2} -> + {NewState, Result} = Module:read_exact(Transport#t_transport.state, Len), + {Transport#t_transport{state = NewState}, Result}; + _ -> + read(Transport, Len) + end. + + +write(Transport = #t_transport{module = Module}, Data) -> + {NewState, Result} = Module:write(Transport#t_transport.state, Data), + {Transport#t_transport{state = NewState}, Result}. + + +flush(Transport = #t_transport{module = Module}) -> + {NewState, Result} = Module:flush(Transport#t_transport.state), + {Transport#t_transport{state = NewState}, Result}. + + +close(Transport = #t_transport{module = Module}) -> + {NewState, Result} = Module:close(Transport#t_transport.state), + {Transport#t_transport{state = NewState}, Result}. + diff --git a/src/jaegertracing/thrift/lib/erl/src/thrift_transport_state_test.erl b/src/jaegertracing/thrift/lib/erl/src/thrift_transport_state_test.erl new file mode 100644 index 000000000..e83a44d26 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/src/thrift_transport_state_test.erl @@ -0,0 +1,117 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(thrift_transport_state_test). + +-behaviour(gen_server). +-behaviour(thrift_transport). + +%% API +-export([new/1]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +%% thrift_transport callbacks +-export([write/2, read/2, flush/1, close/1]). + +-record(trans, {wrapped, % #thrift_transport{} + version :: integer(), + counter :: pid() + }). +-type state() :: #trans{}. +-include("thrift_transport_behaviour.hrl"). + +-record(state, {cversion :: integer()}). + + +new(WrappedTransport) -> + case gen_server:start_link(?MODULE, [], []) of + {ok, Pid} -> + Trans = #trans{wrapped = WrappedTransport, + version = 0, + counter = Pid}, + thrift_transport:new(?MODULE, Trans); + Else -> + Else + end. + +%%==================================================================== +%% thrift_transport callbacks +%%==================================================================== + +write(Transport0 = #trans{wrapped = Wrapped0}, Data) -> + Transport1 = check_version(Transport0), + {Wrapped1, Result} = thrift_transport:write(Wrapped0, Data), + Transport2 = Transport1#trans{wrapped = Wrapped1}, + {Transport2, Result}. + +flush(Transport0 = #trans{wrapped = Wrapped0}) -> + Transport1 = check_version(Transport0), + {Wrapped1, Result} = thrift_transport:flush(Wrapped0), + Transport2 = Transport1#trans{wrapped = Wrapped1}, + {Transport2, Result}. + +close(Transport0 = #trans{wrapped = Wrapped0}) -> + Transport1 = check_version(Transport0), + shutdown_counter(Transport1), + {Wrapped1, Result} = thrift_transport:close(Wrapped0), + Transport2 = Transport1#trans{wrapped = Wrapped1}, + {Transport2, Result}. + +read(Transport0 = #trans{wrapped = Wrapped0}, Len) -> + Transport1 = check_version(Transport0), + {Wrapped1, Result} = thrift_transport:read(Wrapped0, Len), + Transport2 = Transport1#trans{wrapped = Wrapped1}, + {Transport2, Result}. + + +%%==================================================================== +%% gen_server callbacks +%%==================================================================== + +init([]) -> + {ok, #state{cversion = 0}}. + +handle_call(check_version, _From, State = #state{cversion = Version}) -> + {reply, Version, State#state{cversion = Version+1}}. + +handle_cast(shutdown, State) -> + {stop, normal, State}. + +handle_info(_Info, State) -> {noreply, State}. +code_change(_OldVsn, State, _Extra) -> {ok, State}. +terminate(_Reason, _State) -> ok. + +%%-------------------------------------------------------------------- +%% Internal functions +%%-------------------------------------------------------------------- + +check_version(Transport = #trans{version = Version, counter = Counter}) -> + case gen_server:call(Counter, check_version) of + Version -> + Transport#trans{version = Version+1}; + _Else -> + % State wasn't propagated properly. Die. + erlang:error(state_not_propagated) + end. + +shutdown_counter(#trans{counter = Counter}) -> + gen_server:cast(Counter, shutdown). diff --git a/src/jaegertracing/thrift/lib/erl/test/Thrift1151.thrift b/src/jaegertracing/thrift/lib/erl/test/Thrift1151.thrift new file mode 100644 index 000000000..6f934a7b1 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/Thrift1151.thrift @@ -0,0 +1,3 @@ +struct StructA { 1: i16 x; } +struct StructB { 1: i32 x; } +struct StructC { 1: StructA x; } diff --git a/src/jaegertracing/thrift/lib/erl/test/Thrift1475.thrift b/src/jaegertracing/thrift/lib/erl/test/Thrift1475.thrift new file mode 100644 index 000000000..7adeb7e65 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/Thrift1475.thrift @@ -0,0 +1,34 @@ +struct StructB +{ + 1: string x +} + +struct StructA +{ + 1: string a, + 2: binary b, + 3: optional string c, + 4: optional binary d, + 5: required string e, + 6: required binary f, + 7: string g = "foo", + 8: i32 h, + 9: optional i32 i, + 10: required i32 j, + 11: required i32 k = 5, + 12: double l, + 13: optional double m, + 14: required double n, + 15: double o = 3.14159, + 16: list string_list, + 17: list byte_list = [1, 2, 3], + 18: required list rsl, + 19: optional list osl, + 20: set string_set, + 21: required set rss, + 22: optional set oss, + 23: map string_map, + 24: required map rsm, + 25: optional map osm, + 26: StructB structb +} diff --git a/src/jaegertracing/thrift/lib/erl/test/Thrift_omit_with.thrift b/src/jaegertracing/thrift/lib/erl/test/Thrift_omit_with.thrift new file mode 100644 index 000000000..8bffc7c73 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/Thrift_omit_with.thrift @@ -0,0 +1,22 @@ +struct test1 { + 1: i32 one + 2: i32 two // omit + 3: i32 three +} + +struct test2 { + 1: i32 one + 2: test2 two // omit + 3: i32 three +} + +struct test3 { + 1: i32 one + 2: list two // omit +} + +struct test4 { + 1: i32 one + 2: map two // omit +} + diff --git a/src/jaegertracing/thrift/lib/erl/test/flags/LegacyNames.thrift b/src/jaegertracing/thrift/lib/erl/test/flags/LegacyNames.thrift new file mode 100644 index 000000000..38f272907 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/flags/LegacyNames.thrift @@ -0,0 +1,33 @@ +enum Numberz +{ + ONE = 1, + TWO, + THREE, + FIVE = 5, + SIX, + EIGHT = 8 +} + +const Numberz myNumberz = Numberz.ONE; + +struct CapitalizedStruct +{ + 1: i32 Id, + 2: binary message +} + +struct ListCapitalizedStructs +{ + 1: list structs +} + +exception Xception { + 1: i32 errorCode, + 2: binary message +} + +service LegacyNames +{ + ListCapitalizedStructs Names(1: CapitalizedStruct foo, 2: CapitalizedStruct bar) + throws(1: Xception err) +} \ No newline at end of file diff --git a/src/jaegertracing/thrift/lib/erl/test/flags/Thrift3214.thrift b/src/jaegertracing/thrift/lib/erl/test/flags/Thrift3214.thrift new file mode 100644 index 000000000..a9110cedd --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/flags/Thrift3214.thrift @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +struct StringMap +{ + 1: map data = {1: "a", 2: "b"}; +} diff --git a/src/jaegertracing/thrift/lib/erl/test/legacy_names_test.erl b/src/jaegertracing/thrift/lib/erl/test/legacy_names_test.erl new file mode 100644 index 000000000..c16aa3ee2 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/legacy_names_test.erl @@ -0,0 +1,69 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(legacy_names_test). +-compile(export_all). + +-include_lib("eunit/include/eunit.hrl"). + +-include("gen-erl/legacyNames_constants.hrl"). + +record_generation_test_() -> + [ + {"capitalizedStruct record", ?_assertMatch( + {capitalizedStruct, _, _}, + #capitalizedStruct{id=null,message=null} + )} + ]. + +struct_info_test_() -> + [ + {"capitalizedStruct extended definition", ?_assertEqual( + {struct, [ + {1, undefined, i32, 'id', undefined}, + {2, undefined, string, 'message', undefined} + ]}, + legacyNames_types:struct_info_ext(capitalizedStruct) + )}, + {"listCapitalizedStructs extended definition", ?_assertEqual( + {struct, [ + {1, undefined, {list, {struct, {'legacyNames_types', 'capitalizedStruct'}}}, 'structs', []} + ]}, + legacyNames_types:struct_info_ext(listCapitalizedStructs) + )} + ]. + +service_info_test_() -> + [ + {"names params", ?_assertEqual( + {struct, [ + {1, {struct, {'legacyNames_types', 'capitalizedStruct'}}}, + {2, {struct, {'legacyNames_types', 'capitalizedStruct'}}} + ]}, + legacyNames_thrift:function_info(names, params_type) + )}, + {"names reply", ?_assertEqual( + {struct, {'legacyNames_types', 'listCapitalizedStructs'}}, + legacyNames_thrift:function_info(names, reply_type) + )}, + {"names exceptions", ?_assertEqual( + {struct, [{1, {struct, {'legacyNames_types', 'xception'}}}]}, + legacyNames_thrift:function_info(names, exceptions) + )} + ]. diff --git a/src/jaegertracing/thrift/lib/erl/test/multiplexing.thrift b/src/jaegertracing/thrift/lib/erl/test/multiplexing.thrift new file mode 100644 index 000000000..7c7994b97 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/multiplexing.thrift @@ -0,0 +1,7 @@ +service Multiplexing_Calculator { + i32 add(1: i32 x, 2: i32 y) +} + +service Multiplexing_WeatherReport { + double getTemperature() +} diff --git a/src/jaegertracing/thrift/lib/erl/test/multiplexing_test.erl b/src/jaegertracing/thrift/lib/erl/test/multiplexing_test.erl new file mode 100644 index 000000000..0f2d616b8 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/multiplexing_test.erl @@ -0,0 +1,57 @@ +-module(multiplexing_test). + +-include_lib("eunit/include/eunit.hrl"). + +-export([ + handle_function/2 + ,handle_error/2 +]). + +start_multiplexed_server_test() -> + + Port = 9090, + Services = [ + {"Multiplexing_Calculator", multiplexing__calculator_thrift}, + {"Multiplexing_WeatherReport", multiplexing__weather_report_thrift} + ], + + {ok, Pid} = thrift_socket_server:start([ + {ip, "127.0.0.1"}, + {port, Port}, + {name, ?MODULE}, + {service, Services}, + {handler, [ + {"error_handler", ?MODULE}, + {"Multiplexing_Calculator", ?MODULE}, + {"Multiplexing_WeatherReport", ?MODULE} + ]} + ]), + + {ok, [{"Multiplexing_Calculator", CalculatorClient0}, + {"Multiplexing_WeatherReport", WeatherReportClient0}]} = thrift_client_util:new_multiplexed("127.0.0.1", Port, Services, []), + + ?assertMatch({_, {error, {bad_args, _, _}}}, thrift_client:call(WeatherReportClient0, getTemperature, [1])), + ?assertMatch({_, {error, {bad_args, _, _}}}, thrift_client:call(CalculatorClient0, add, [1])), + ?assertMatch({_, {error, {bad_args, _, _}}}, thrift_client:call(CalculatorClient0, add, [1,1,1])), + + ?assertMatch({_, {error, {no_function, _}}}, thrift_client:call(CalculatorClient0, getTemperature, [])), + ?assertMatch({_, {error, {no_function, _}}}, thrift_client:call(WeatherReportClient0, add, [41, 1])), + + ?assertMatch({_, {ok, 42}}, thrift_client:call(CalculatorClient0, add, [41, 1])), + ?assertMatch({_, {ok, 42.0}}, thrift_client:call(WeatherReportClient0, getTemperature, [])), + + thrift_socket_server:stop(Pid). + +%% HANDLE FUNCTIONS + +%% Calculator handles +handle_function(add, {X, Y}) -> + {reply, X + Y}; + +%% WeatherReport handles +handle_function(getTemperature, {}) -> + {reply, 42.0}. + +handle_error(_F, _Reason) -> +%% ?debugHere, ?debugVal({_F, _Reason}), + ok. \ No newline at end of file diff --git a/src/jaegertracing/thrift/lib/erl/test/name_conflict_test.erl b/src/jaegertracing/thrift/lib/erl/test/name_conflict_test.erl new file mode 100644 index 000000000..b01df5732 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/name_conflict_test.erl @@ -0,0 +1,299 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(name_conflict_test). +-compile(export_all). + +-include_lib("eunit/include/eunit.hrl"). + +-include("gen-erl/name_conflict_test_constants.hrl"). + +record_generation_test_() -> + [ + {"using record", ?_assertMatch( + {using, _, _}, + #using{single=null,integer=null} + )}, + {"delegate record", ?_assertMatch( + {delegate, _, _}, + #delegate{partial=null,delegate=null} + )}, + {"get record", ?_assertMatch( + {get, _}, + #get{sbyte=null} + )}, + {"partial record", ?_assertMatch( + {partial, _, _, _}, + #partial{using=null} + )}, + {"ClassAndProp record", ?_assertMatch( + {'ClassAndProp', _, _, _, _}, + #'ClassAndProp'{ + 'ClassAndProp'=null, + 'ClassAndProp_'=null, + 'ClassAndProp__'=null, + 'ClassAndProper'=null + } + )}, + {"second_chance record", ?_assertMatch( + {second_chance, _, _, _, _}, + #second_chance{ + 'SECOND_CHANCE'=null, + 'SECOND_CHANCE_'=null, + 'SECOND_CHANCE__'=null, + 'SECOND_CHANCES'=null + } + )}, + {"NOW_EAT_THIS record", ?_assertMatch( + {'NOW_EAT_THIS', _, _, _, _}, + #'NOW_EAT_THIS'{ + now_eat_this=null, + now_eat_this_=null, + now_eat_this__=null, + now_eat_this_and_this=null + } + )}, + {"TheEdgeCase record", ?_assertMatch( + {'TheEdgeCase', _, _, _, _, _, _}, + #'TheEdgeCase'{ + theEdgeCase=null, + theEdgeCase_=null, + theEdgeCase__=null, + 'TheEdgeCase'=null, + 'TheEdgeCase_'=null, + 'TheEdgeCase__'=null + } + )}, + {"Tricky_ record", ?_assertMatch( + {'Tricky_', _, _}, + #'Tricky_'{tricky=null,'Tricky'=null} + )}, + {"Nested record", ?_assertMatch( + {'Nested', _, _, _, _, _, _}, + #'Nested'{ + 'ClassAndProp'=null, + second_chance=null, + 'NOW_EAT_THIS'=null, + 'TheEdgeCase'=null, + 'Tricky_'=null, + 'Nested'=null + } + )}, + {"Problem_ record", ?_assertMatch( + {'Problem_', _, _}, + #'Problem_'{problem=null,'Problem'=null} + )} + ]. + +struct_info_test_() -> + [ + {"using definition", ?_assertEqual( + {struct, [{1, double},{2, double}]}, + name_conflict_test_types:struct_info(using) + )}, + {"delegate definition", ?_assertEqual( + {struct, [ + {1, string}, + {2, {struct, {name_conflict_test_types, delegate}}} + ]}, + name_conflict_test_types:struct_info(delegate) + )}, + {"get definition", ?_assertEqual( + {struct, [{1, bool}]}, + name_conflict_test_types:struct_info(get) + )}, + {"partial definition", ?_assertEqual( + {struct, [ + {1, {struct, {name_conflict_test_types, using}}}, + {2, bool}, + {3, bool} + ]}, + name_conflict_test_types:struct_info(partial) + )}, + {"ClassAndProp definition", ?_assertEqual( + {struct, [{1, bool},{2, bool},{3, bool},{4, bool}]}, + name_conflict_test_types:struct_info('ClassAndProp') + )}, + {"second_chance definition", ?_assertEqual( + {struct, [{1, bool},{2, bool},{3, bool},{4, bool}]}, + name_conflict_test_types:struct_info(second_chance) + )}, + {"NOW_EAT_THIS definition", ?_assertEqual( + {struct, [{1, bool},{2, bool},{3, bool},{4, bool}]}, + name_conflict_test_types:struct_info('NOW_EAT_THIS') + )}, + {"TheEdgeCase definition", ?_assertEqual( + {struct, [{1, bool},{2, bool},{3, bool},{4, bool},{5, bool},{6, bool}]}, + name_conflict_test_types:struct_info('TheEdgeCase') + )}, + {"Tricky_ definition", ?_assertEqual( + {struct, [{1, bool},{2, bool}]}, + name_conflict_test_types:struct_info('Tricky_') + )}, + {"Nested definition", ?_assertEqual( + {struct, [ + {1, {struct, {name_conflict_test_types, 'ClassAndProp'}}}, + {2, {struct, {name_conflict_test_types, second_chance}}}, + {3, {struct, {name_conflict_test_types, 'NOW_EAT_THIS'}}}, + {4, {struct, {name_conflict_test_types, 'TheEdgeCase'}}}, + {5, {struct, {name_conflict_test_types, 'Tricky_'}}}, + {6, {struct, {name_conflict_test_types, 'Nested'}}} + ]}, + name_conflict_test_types:struct_info('Nested') + )}, + {"Problem_ definition", ?_assertEqual( + {struct, [{1, bool},{2, bool}]}, + name_conflict_test_types:struct_info('Problem_') + )}, + {"using extended definition", ?_assertEqual( + {struct, [ + {1, undefined, double, single, undefined}, + {2, undefined, double, integer, undefined} + ]}, + name_conflict_test_types:struct_info_ext(using) + )}, + {"delegate extended definition", ?_assertEqual( + {struct, [ + {1, undefined, string, partial, undefined}, + {2, undefined, {struct, {name_conflict_test_types, delegate}}, delegate, undefined} + ]}, + name_conflict_test_types:struct_info_ext(delegate) + )}, + {"get extended definition", ?_assertEqual( + {struct, [{1, undefined, bool, sbyte, undefined}]}, + name_conflict_test_types:struct_info_ext(get) + )}, + {"partial extended definition", ?_assertEqual( + {struct, [ + {1, undefined, {struct, {name_conflict_test_types, using}}, using, #using{}}, + {2, undefined, bool, read, undefined}, + {3, undefined, bool, write, undefined} + ]}, + name_conflict_test_types:struct_info_ext(partial) + )}, + {"ClassAndProp extended definition", ?_assertEqual( + {struct, [ + {1, undefined, bool, 'ClassAndProp', undefined}, + {2, undefined, bool, 'ClassAndProp_', undefined}, + {3, undefined, bool, 'ClassAndProp__', undefined}, + {4, undefined, bool, 'ClassAndProper', undefined} + ]}, + name_conflict_test_types:struct_info_ext('ClassAndProp') + )}, + {"second_chance extended definition", ?_assertEqual( + {struct, [ + {1, undefined, bool, 'SECOND_CHANCE', undefined}, + {2, undefined, bool, 'SECOND_CHANCE_', undefined}, + {3, undefined, bool, 'SECOND_CHANCE__', undefined}, + {4, undefined, bool, 'SECOND_CHANCES', undefined} + ]}, + name_conflict_test_types:struct_info_ext(second_chance) + )}, + {"NOW_EAT_THIS extended definition", ?_assertEqual( + {struct, [ + {1, undefined, bool, now_eat_this, undefined}, + {2, undefined, bool, now_eat_this_, undefined}, + {3, undefined, bool, now_eat_this__, undefined}, + {4, undefined, bool, now_eat_this_and_this, undefined} + ]}, + name_conflict_test_types:struct_info_ext('NOW_EAT_THIS') + )}, + {"TheEdgeCase extended definition", ?_assertEqual( + {struct, [ + {1, undefined, bool, theEdgeCase, undefined}, + {2, undefined, bool, theEdgeCase_, undefined}, + {3, undefined, bool, theEdgeCase__, undefined}, + {4, undefined, bool, 'TheEdgeCase', undefined}, + {5, undefined, bool, 'TheEdgeCase_', undefined}, + {6, undefined, bool, 'TheEdgeCase__', undefined} + ]}, + name_conflict_test_types:struct_info_ext('TheEdgeCase') + )}, + {"Tricky_ extended definition", ?_assertEqual( + {struct, [ + {1, undefined, bool, tricky, undefined}, + {2, undefined, bool, 'Tricky', undefined} + ]}, + name_conflict_test_types:struct_info_ext('Tricky_') + )}, + {"Nested extended definition", ?_assertEqual( + {struct, [ + {1, undefined, {struct, { + name_conflict_test_types, + 'ClassAndProp' + }}, 'ClassAndProp', #'ClassAndProp'{}}, + {2, undefined, {struct, { + name_conflict_test_types, + second_chance + }}, second_chance, #second_chance{}}, + {3, undefined, {struct, { + name_conflict_test_types, + 'NOW_EAT_THIS' + }}, 'NOW_EAT_THIS', #'NOW_EAT_THIS'{}}, + {4, undefined, {struct, { + name_conflict_test_types, + 'TheEdgeCase' + }}, 'TheEdgeCase', #'TheEdgeCase'{}}, + {5, undefined, {struct, { + name_conflict_test_types, + 'Tricky_' + }}, 'Tricky_', #'Tricky_'{}}, + {6, undefined, {struct, { + name_conflict_test_types, + 'Nested' + }}, 'Nested', undefined} + ]}, + name_conflict_test_types:struct_info_ext('Nested') + )}, + {"Problem_ extended definition", ?_assertEqual( + {struct, [ + {1, undefined, bool, problem, undefined}, + {2, undefined, bool, 'Problem', undefined} + ]}, + name_conflict_test_types:struct_info_ext('Problem_') + )} + ]. + +service_info_test_() -> + [ + {"event params", ?_assertEqual( + {struct, [{1, {struct, {name_conflict_test_types, partial}}}]}, + extern_thrift:function_info(event, params_type) + )}, + {"event reply", ?_assertEqual( + {struct, {name_conflict_test_types, delegate}}, + extern_thrift:function_info(event, reply_type) + )}, + {"event exceptions", ?_assertEqual( + {struct, []}, + extern_thrift:function_info(event, exceptions) + )}, + {"Foo params", ?_assertEqual( + {struct, [{1, {struct, {name_conflict_test_types, 'Nested'}}}]}, + extern_thrift:function_info('Foo', params_type) + )}, + {"Foo reply", ?_assertEqual( + {struct, []}, + extern_thrift:function_info('Foo', reply_type) + )}, + {"Foo exceptions", ?_assertEqual( + {struct, [{1, {struct, {name_conflict_test_types, 'Problem_'}}}]}, + extern_thrift:function_info('Foo', exceptions) + )} + ]. diff --git a/src/jaegertracing/thrift/lib/erl/test/stress_server.erl b/src/jaegertracing/thrift/lib/erl/test/stress_server.erl new file mode 100644 index 000000000..35fff0693 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/stress_server.erl @@ -0,0 +1,64 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(stress_server). + + +-export([start_link/1, + + handle_function/2, + + echoVoid/0, + echoByte/1, + echoI32/1, + echoI64/1, + echoString/1, + echoList/1, + echoSet/1, + echoMap/1 + ]). + +start_link(Port) -> + thrift_server:start_link(Port, service_thrift, ?MODULE). + + +handle_function(Function, Args) -> + case apply(?MODULE, Function, tuple_to_list(Args)) of + ok -> + ok; + Else -> {reply, Else} + end. + + +echoVoid() -> + ok. +echoByte(X) -> + X. +echoI32(X) -> + X. +echoI64(X) -> + X. +echoString(X) -> + X. +echoList(X) -> + X. +echoSet(X) -> + X. +echoMap(X) -> + X. diff --git a/src/jaegertracing/thrift/lib/erl/test/test_const.erl b/src/jaegertracing/thrift/lib/erl/test/test_const.erl new file mode 100644 index 000000000..627777baa --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/test_const.erl @@ -0,0 +1,54 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(test_const). + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + +-include("gen-erl/constants_demo_types.hrl"). + +namespace_test() -> + %% Verify that records produced by ConstantsDemo.thrift have the right namespace. + io:format(user, "in namespace_test()\n", []), + {struct, _} = constants_demo_types:struct_info('consts_thing'), + {struct, _} = constants_demo_types:struct_info('consts_Blah'), + ok. + +const_map_test() -> + ?assertEqual(233, constants_demo_constants:gen_map(35532)), + ?assertError(function_clause, constants_demo_constants:gen_map(0)), + + ?assertEqual(853, constants_demo_constants:gen_map(43523, default)), + ?assertEqual(default, constants_demo_constants:gen_map(10110, default)), + + ?assertEqual(98325, constants_demo_constants:gen_map2("lkjsdf")), + ?assertError(function_clause, constants_demo_constants:gen_map2("nonexist")), + + ?assertEqual(233, constants_demo_constants:gen_map2("hello", 321)), + ?assertEqual(321, constants_demo_constants:gen_map2("goodbye", 321)). + +const_list_test() -> + ?assertEqual(23598352, constants_demo_constants:gen_list(2)), + ?assertError(function_clause, constants_demo_constants:gen_list(0)), + + ?assertEqual(3253523, constants_demo_constants:gen_list(3, default)), + ?assertEqual(default, constants_demo_constants:gen_list(10, default)). + +-endif. %% TEST diff --git a/src/jaegertracing/thrift/lib/erl/test/test_disklog.erl b/src/jaegertracing/thrift/lib/erl/test/test_disklog.erl new file mode 100644 index 000000000..dcb6fc1b9 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/test_disklog.erl @@ -0,0 +1,99 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(test_disklog). + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + +disklog_test() -> + {ok, TransportFactory} = + thrift_disk_log_transport:new_transport_factory( + test_disklog, + [{file, "./test_log"}, + {size, {1024*1024, 10}}]), + {ok, ProtocolFactory} = + thrift_binary_protocol:new_protocol_factory( TransportFactory, []), + {ok, Proto} = ProtocolFactory(), + {ok, Client0} = thrift_client:new(Proto, thrift_test_thrift), + + io:format("Client started~n"), + + % We have to make oneway calls into this client only since otherwise it + % will try to read from the disklog and go boom. + {Client1, {ok, ok}} = thrift_client:call(Client0, testOneway, [16#deadbeef]), + io:format("Call written~n"), + + % Use the send_call method to write a non-oneway call into the log + {Client2, ok} = + thrift_client:send_call(Client1, testString, [<<"hello world">>]), + io:format("Non-oneway call sent~n"), + + {_Client3, ok} = thrift_client:close(Client2), + io:format("Client closed~n"), + + lists:foreach(fun(File) -> file:delete(File) end, [ + "./test_log.1", + "./test_log.idx", + "./test_log.siz" + ]), + io:format("Cleaning up test files~n"), + + ok. + +disklog_base64_test() -> + {ok, TransportFactory} = + thrift_disk_log_transport:new_transport_factory( + test_disklog, + [{file, "./test_b64_log"}, + {size, {1024*1024, 10}}]), + {ok, B64Factory} = + thrift_base64_transport:new_transport_factory(TransportFactory), + {ok, BufFactory} = + thrift_buffered_transport:new_transport_factory(B64Factory), + {ok, ProtocolFactory} = + thrift_binary_protocol:new_protocol_factory(BufFactory, []), + {ok, Proto} = ProtocolFactory(), + {ok, Client0} = thrift_client:new(Proto, thrift_test_thrift), + + io:format("Client started~n"), + + % We have to make oneway calls into this client only since otherwise + % it will try to read from the disklog and go boom. + {Client1, {ok, ok}} = thrift_client:call(Client0, testOneway, [16#deadbeef]), + io:format("Call written~n"), + + % Use the send_call method to write a non-oneway call into the log + {Client2, ok} = + thrift_client:send_call(Client1, testString, [<<"hello world">>]), + io:format("Non-oneway call sent~n"), + + {_Client3, ok} = thrift_client:close(Client2), + io:format("Client closed~n"), + + lists:foreach(fun(File) -> file:delete(File) end, [ + "./test_b64_log.1", + "./test_b64_log.idx", + "./test_b64_log.siz" + ]), + io:format("Cleaning up test files~n"), + + ok. + +-endif. diff --git a/src/jaegertracing/thrift/lib/erl/test/test_omit.erl b/src/jaegertracing/thrift/lib/erl/test/test_omit.erl new file mode 100644 index 000000000..80841e291 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/test_omit.erl @@ -0,0 +1,79 @@ +-module(test_omit). + +-include("gen-erl/thrift_omit_with_types.hrl"). + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + +omit_struct1_test() -> + %% In this test, the field that is deleted is a basic type (an i32). + A = #test1{one = 1, three = 3}, + B = #test1{one = 1, two = 2, three = 3}, + {ok, Transport} = thrift_membuffer_transport:new(), + {ok, P0} = thrift_binary_protocol:new(Transport), + + {P1, ok} = thrift_protocol:write(P0, {{struct, {thrift_omit_with_types, element(1, A)}}, A}), + {P2, {ok, O0}} = thrift_protocol:read(P1, {struct, {thrift_omit_without_types, element(1, A)}}), + ?assertEqual(element(1, A), element(1, O0)), + ?assertEqual(element(2, A), element(2, O0)), + ?assertEqual(element(4, A), element(3, O0)), + + {P3, ok} = thrift_protocol:write(P2, {{struct, {thrift_omit_with_types, element(1, B)}}, B}), + {_P4, {ok, O1}} = thrift_protocol:read(P3, {struct, {thrift_omit_without_types, element(1, A)}}), + ?assertEqual(element(1, A), element(1, O1)), + ?assertEqual(element(2, A), element(2, O1)), + ?assertEqual(element(4, A), element(3, O1)), + + ok. + +omit_struct2_test() -> + %% In this test, the field that is deleted is a struct. + A = #test2{one = 1, two = #test2{one = 10, three = 30}, three = 3}, + B = #test2{one = 1, two = #test2{one = 10, two = #test2{one = 100}, three = 30}, three = 3}, + + {ok, Transport} = thrift_membuffer_transport:new(), + {ok, P0} = thrift_binary_protocol:new(Transport), + + {P1, ok} = thrift_protocol:write(P0, {{struct, {thrift_omit_with_types, element(1, A)}}, A}), + {P2, {ok, O0}} = thrift_protocol:read(P1, {struct, {thrift_omit_without_types, element(1, A)}}), + ?assertEqual(element(1, A), element(1, O0)), + ?assertEqual(element(2, A), element(2, O0)), + ?assertEqual(element(4, A), element(3, O0)), + + {P3, ok} = thrift_protocol:write(P2, {{struct, {thrift_omit_with_types, element(1, B)}}, B}), + {_P4, {ok, O1}} = thrift_protocol:read(P3, {struct, {thrift_omit_without_types, element(1, A)}}), + ?assertEqual(element(1, A), element(1, O1)), + ?assertEqual(element(2, A), element(2, O1)), + ?assertEqual(element(4, A), element(3, O1)), + + ok. + +omit_list_test() -> + %% In this test, the field that is deleted is a list. + A = #test1{one = 1, two = 2, three = 3}, + B = #test3{one = 1, two = [ A ]}, + + {ok, Transport} = thrift_membuffer_transport:new(), + {ok, P0} = thrift_binary_protocol:new(Transport), + + {P1, ok} = thrift_protocol:write(P0, {{struct, {thrift_omit_with_types, element(1, B)}}, B}), + {_P2, {ok, O0}} = thrift_protocol:read(P1, {struct, {thrift_omit_without_types, element(1, B)}}), + ?assertEqual(element(2, B), element(2, O0)), + + ok. + +omit_map_test() -> + %% In this test, the field that is deleted is a map. + A = #test1{one = 1, two = 2, three = 3}, + B = #test4{one = 1, two = dict:from_list([ {2, A} ])}, + + {ok, Transport} = thrift_membuffer_transport:new(), + {ok, P0} = thrift_binary_protocol:new(Transport), + + {P1, ok} = thrift_protocol:write(P0, {{struct, {thrift_omit_with_types, element(1, B)}}, B}), + {_P2, {ok, O0}} = thrift_protocol:read(P1, {struct, {thrift_omit_without_types, element(1, B)}}), + ?assertEqual(element(2, B), element(2, O0)), + + ok. + +-endif. %% TEST diff --git a/src/jaegertracing/thrift/lib/erl/test/test_rendered_double_constants.erl b/src/jaegertracing/thrift/lib/erl/test/test_rendered_double_constants.erl new file mode 100644 index 000000000..87fce8130 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/test_rendered_double_constants.erl @@ -0,0 +1,68 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(test_rendered_double_constants). + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + +-include("gen-erl/double_constants_test_constants.hrl"). + +-define(EPSILON, 0.0000001). + +rendered_double_constants_test() -> + ?assert(abs(1.0 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST) =< ?EPSILON), + ?assert(abs(-100.0 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST) =< ?EPSILON), + ?assert(abs(9223372036854775807.0 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST) =< ?EPSILON), + ?assert(abs(-9223372036854775807.0 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST) =< ?EPSILON), + ?assert(abs(3.14159265359 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST) =< ?EPSILON), + ?assert(abs(1000000.1 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST) =< ?EPSILON), + ?assert(abs(-1000000.1 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST) =< ?EPSILON), + ?assert(abs(1.7e+308 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST) =< ?EPSILON), + ?assert(abs(9223372036854775816.43 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST) =< ?EPSILON), + ?assert(abs(-1.7e+308 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST) =< ?EPSILON), + ?assert(abs(-9223372036854775816.43 - ?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST) =< ?EPSILON), + ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST)), + ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST)), + ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST)), + ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST)), + ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST)), + ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST)), + ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST)), + ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST)), + ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST)), + ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST)), + ?assert(is_float(?DOUBLE_CONSTANTS_TEST_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST)). + +rendered_double_list_test() -> + ?assertEqual(12, length(?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)), + ?assert(abs(1.0 - lists:nth(1, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON), + ?assert(abs(-100.0 - lists:nth(2, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON), + ?assert(abs(100.0 - lists:nth(3, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON), + ?assert(abs(9223372036854775807.0 - lists:nth(4, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON), + ?assert(abs(-9223372036854775807.0 - lists:nth(5, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON), + ?assert(abs(3.14159265359 - lists:nth(6, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON), + ?assert(abs(1000000.1 - lists:nth(7, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON), + ?assert(abs(-1000000.1 - lists:nth(8, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON), + ?assert(abs(1.7e+308 - lists:nth(9, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON), + ?assert(abs(-1.7e+308 - lists:nth(10, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON), + ?assert(abs(9223372036854775816.43 - lists:nth(11, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON), + ?assert(abs(-9223372036854775816.43 - lists:nth(12, ?DOUBLE_CONSTANTS_TEST_DOUBLE_LIST_TEST)) =< ?EPSILON). + +-endif. %% TEST \ No newline at end of file diff --git a/src/jaegertracing/thrift/lib/erl/test/test_thrift_1151.erl b/src/jaegertracing/thrift/lib/erl/test/test_thrift_1151.erl new file mode 100644 index 000000000..f4a910e7d --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/test_thrift_1151.erl @@ -0,0 +1,34 @@ +-module(test_thrift_1151). + +-include("gen-erl/thrift1151_types.hrl"). + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + +unmatched_struct_test() -> + S1 = #'StructC'{x=#'StructB'{x=1}}, + {ok, Transport} = thrift_memory_buffer:new(), + {ok, Protocol} = thrift_binary_protocol:new(Transport), + ?assertException( + error, + struct_unmatched, + thrift_protocol:write( + Protocol, + {{struct, element(2, thrift1151_types:struct_info('StructC'))}, S1} + ) + ). + +badarg_test() -> + S2 = #'StructC'{x=#'StructA'{x="1"}}, + {ok, Transport} = thrift_memory_buffer:new(), + {ok, Protocol} = thrift_binary_protocol:new(Transport), + ?assertException( + error, + badarg, + thrift_protocol:write( + Protocol, + {{struct, element(2, thrift1151_types:struct_info('StructC'))}, S2} + ) + ). + +-endif. diff --git a/src/jaegertracing/thrift/lib/erl/test/test_thrift_3214.erl b/src/jaegertracing/thrift/lib/erl/test/test_thrift_3214.erl new file mode 100644 index 000000000..0f9544bef --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/test_thrift_3214.erl @@ -0,0 +1,60 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(test_thrift_3214). +-compile(export_all). + +-include("gen-erl/thrift3214_types.hrl"). + +-ifdef(TEST). +-ifndef(otp16_or_less). +-include_lib("eunit/include/eunit.hrl"). + +record_generation_test_() -> + [ + {"StringMap record", ?_assertMatch( + {'StringMap', _}, + #'StringMap'{data=#{50 => "foo"}} + )}, + {"StringMap record defaults", ?_assertEqual( + {'StringMap', #{1 => "a", 2 => "b"}}, + #'StringMap'{} + )}, + {"StringMap record dict from list", ?_assertNotEqual( + {'StringMap', dict:from_list([{1, "a"}, {2, "b"}])}, + #'StringMap'{} + )}, + {"StringMap record map from list", ?_assertEqual( + {'StringMap', maps:from_list([{1, "a"}, {2, "b"}])}, + #'StringMap'{} + )} + ]. + +struct_info_test_() -> + [ + {"StringMap extended definition", ?_assertEqual( + {struct, [ + {1, undefined, {map, i32, string}, 'data', #{1 => "a", 2 => "b"}} + ]}, + thrift3214_types:struct_info_ext('StringMap') + )} + ]. + +-endif. +-endif. diff --git a/src/jaegertracing/thrift/lib/erl/test/test_thrift_buffered_transport.erl b/src/jaegertracing/thrift/lib/erl/test/test_thrift_buffered_transport.erl new file mode 100644 index 000000000..8519e82a1 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/test_thrift_buffered_transport.erl @@ -0,0 +1,359 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(test_thrift_buffered_transport). +-include_lib("eunit/include/eunit.hrl"). + + +new(Transport) -> thrift_buffered_transport:new(Transport). + +new_test_() -> + [ + {"new buffered membuffer", ?_assertMatch( + {ok, {t_transport, thrift_buffered_transport, {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, []}}, + [] + }}}, + new({t_transport, thrift_membuffer_transport, {t_membuffer, []}}) + )} + ]. + + +read(Frame, Bytes) -> thrift_buffered_transport:read(Frame, Bytes). + +read_test_() -> + [ + {"read zero bytes from an empty buffered membuffer", ?_assertMatch( + { + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [] + }, + {ok, <<>>} + }, + read( + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [] + }, + 0 + ) + )}, + {"read 1 byte from an empty buffered membuffer", ?_assertMatch( + {_, {ok, <<>>}}, + read( + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [] + }, + 1 + ) + )}, + {"read zero bytes from nonempty buffered membuffer", ?_assertMatch( + { + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, + <<"hallo world">> + }}, + [] + }, + {ok, <<>>} + }, + read( + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, + <<"hallo world">> + }}, + [] + }, + 0 + ) + )}, + {"read 1 byte from nonempty buffered membuffer", ?_assertMatch( + { + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<"allo world">>}}, + [] + }, + {ok, <<"h">>} + }, + read( + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}}, + [] + }, + 1 + ) + )}, + {"read 1 byte from nonempty buffer", ?_assertMatch( + { + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<"allo world">>}}, + [] + }, + {ok, <<"h">>} + }, + read( + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}}, + [] + }, + 1 + ) + )}, + {"read a zillion bytes from nonempty buffered membuffer", ?_assertMatch( + { + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [] + }, + {ok, <<"hallo world">>} + }, + read( + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}}, + [] + }, + 65536 + ) + )} + ]. + + +read_exact(Frame, Bytes) -> thrift_buffered_transport:read_exact(Frame, Bytes). + +read_exact_test_() -> + [ + {"read exactly zero bytes from an empty buffered membuffer", ?_assertMatch( + { + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [] + }, + {ok, <<>>} + }, + read_exact( + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [] + }, + 0 + ) + )}, + {"read exactly 1 byte from an empty buffered membuffer", ?_assertMatch( + {_, {error, eof}}, + read_exact( + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [] + }, + 1 + ) + )}, + {"read exactly zero bytes from nonempty buffered membuffer", ?_assertMatch( + { + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}}, + [] + }, + {ok, <<>>} + }, + read_exact( + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}}, + [] + }, + 0 + ) + )}, + {"read exactly 1 byte from nonempty buffered membuffer", ?_assertMatch( + { + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<"allo world">>}}, + [] + }, + {ok, <<"h">>} + }, + read_exact( + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, + <<"hallo world">> + }}, + [] + }, + 1 + ) + )}, + {"read exactly 1 byte from nonempty buffer", ?_assertMatch( + { + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<"allo world">>}}, + [] + }, + {ok, <<"h">>} + }, + read_exact( + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}}, + [] + }, + 1 + ) + )}, + {"read exactly a zillion bytes from nonempty buffered membuffer", ?_assertMatch( + { + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<"hallo world">>}}, + [] + }, + {error, eof} + }, + read_exact( + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, + <<"hallo world">> + }}, + [] + }, + 65536 + ) + )} + ]. + + +write(Framed, Data) -> thrift_buffered_transport:write(Framed, Data). + +write_test_() -> + [ + {"write empty list to empty buffered membuffer", ?_assertMatch( + { + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [[], []] + }, + ok + }, + write( + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [] + }, + [] + ) + )}, + {"write empty list to nonempty buffered membuffer", ?_assertMatch( + { + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [["hallo world"], []] + }, + ok + }, + write( + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + ["hallo world"] + }, + [] + ) + )}, + {"write empty binary to empty buffered membuffer", ?_assertMatch( + { + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [[], <<>>] + }, + ok + }, + write( + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [] + }, + <<>> + ) + )}, + {"write empty binary to nonempty buffered membuffer", ?_assertMatch( + { + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [["hallo world"], <<>>] + }, + ok + }, + write( + {t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + ["hallo world"] + }, + <<>> + ) + )} + ]. + + +flush(Transport) -> thrift_buffered_transport:flush(Transport). + +flush_test_() -> + [ + {"flush empty buffered membuffer", ?_assertMatch( + {{t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [] + }, + ok + }, + flush({t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [] + }) + )}, + {"flush nonempty buffered membuffer", ?_assertMatch( + {{t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, + [<<>>, <<"hallo world">>] + }}, + [] + }, + ok + }, + flush({t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + <<"hallo world">> + }) + )} + ]. + + +close(Transport) -> thrift_buffered_transport:close(Transport). + +close_test_() -> + {"close buffered membuffer", ?_assertMatch( + {{t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [] + }, + ok + }, + close({t_buffered, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [] + }) + )}. + diff --git a/src/jaegertracing/thrift/lib/erl/test/test_thrift_compact_protocol.erl b/src/jaegertracing/thrift/lib/erl/test/test_thrift_compact_protocol.erl new file mode 100644 index 000000000..5da78c612 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/test_thrift_compact_protocol.erl @@ -0,0 +1,219 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(test_thrift_compact_protocol). +-include_lib("eunit/include/eunit.hrl"). +-include("thrift_constants.hrl"). +-include("thrift_protocol.hrl"). + + +new(Transport) -> thrift_compact_protocol:new(Transport). +new() -> + {ok, Transport} = thrift_membuffer_transport:new(), + thrift_compact_protocol:new(Transport). + +new_test() -> + new(thrift_membuffer_transport:new()). + +write(This, Value) -> thrift_protocol:write(This, Value). +read(This, Type) -> thrift_protocol:read(This, Type). + +str(This0, Value0) -> + {This1, ok} = write(This0, {string, Value0}), + {This2, {ok, Value1}} = read(This1, string), + ?assertEqual(Value0, binary_to_list(Value1)), + {This2, ok}. +string_test() -> + {ok, This0} = new(), + {This1, ok} = str(This0, "aaa"), + {This2, ok} = str(This1, ""), + {This2, ok}. + +round_trip(This0, Type, Value0) -> + {This1, ok} = write(This0, {Type, Value0}), + {This2, {ok, Value1}} = read(This1, Type), + ?assertEqual(Value0, Value1), + {This2, ok}. + +bool_test() -> + {ok, This0} = new(), + {This1, ok} = round_trip(This0, bool, true), + {This2, ok} = round_trip(This1, bool, false), + {This2, ok}. + +byte(This0, Value0) -> round_trip(This0, byte, Value0). +byte_test() -> + {ok, This0} = new(), + {This1, ok} = byte(This0, 0), + {This2, ok} = byte(This1, 42), + {This3, ok} = byte(This2, -1), + {This4, ok} = byte(This3, -128), + {This4, ok}. + +i16(This0, Value0) -> round_trip(This0, i16, Value0). +i16_test() -> + {ok, This0} = new(), + {This1, ok} = i16(This0, 0), + {This2, ok} = i16(This1, 42), + {This3, ok} = i16(This2, 30000), + {This4, ok} = i16(This3, -1), + {This5, ok} = i16(This4, -128), + {This6, ok} = i16(This5, -30000), + {This6, ok}. + +i32(This0, Value0) -> round_trip(This0, i32, Value0). +i32_test() -> + {ok, This0} = new(), + {This1, ok} = i32(This0, 0), + {This2, ok} = i32(This1, 42), + {This3, ok} = i32(This2, 30000), + {This4, ok} = i32(This3, 2000000002), + {This5, ok} = i32(This4, -1), + {This6, ok} = i32(This5, -128), + {This7, ok} = i32(This6, -30000), + {This8, ok} = i32(This7, -2000000002), + {This8, ok}. + +i64(This0, Value0) -> round_trip(This0, i64, Value0). +i64_test() -> + {ok, This0} = new(), + {This1, ok} = i64(This0, 0), + {This2, ok} = i64(This1, 42), + {This3, ok} = i64(This2, 30000), + {This4, ok} = i64(This3, 2000000002), + {This5, ok} = i64(This4, 100000000000000064), + {This6, ok} = i64(This5, -1), + {This7, ok} = i64(This6, -128), + {This8, ok} = i64(This7, -30000), + {This9, ok} = i64(This8, -2000000002), + {This10, ok} = i64(This9, -100000000000000064), + {This10, ok}. + +struct_test() -> + {ok, P0} = new(), + {P1, ok} = write(P0, #protocol_message_begin{ name = "Message1", type = ?tType_I8, seqid = 3}), + {P2, ok} = write(P1, #protocol_struct_begin{}), + {P3, ok} = write(P2, #protocol_field_begin{ name = "field1", type = ?tType_I8, id = 1}), + {P4, ok} = write(P3, {byte, 42}), + {P5, ok} = write(P4, field_end), + {P6, ok} = write(P5, #protocol_field_begin{ name = "field2", type = ?tType_I8, id = 14}), + {P7, ok} = write(P6, {byte, 3}), + {P8, ok} = write(P7, field_end), + {P9, ok} = write(P8, #protocol_field_begin{ name = "field3", type = ?tType_I8, id = 42}), + {P10, ok} = write(P9, {byte, 8}), + {P11, ok} = write(P10, field_end), + {P12, ok} = write(P11, field_stop), + {P13, ok} = write(P12, struct_end), + {P14, ok} = write(P13, message_end), + + {P15, #protocol_message_begin{ name = "Message1", type = ?tType_I8, seqid = 3}} = read(P14, message_begin), + {P16, ok} = read(P15, struct_begin), + {P17, #protocol_field_begin{ type = ?tType_I8, id = 1 }} = read(P16, field_begin), + {P18, {ok, 42}} = read(P17, byte), + {P19, ok} = read(P18, field_end), + {P20, #protocol_field_begin{ type = ?tType_I8, id = 14 }} = read(P19, field_begin), + {P21, {ok, 3}} = read(P20, byte), + {P22, ok} = read(P21, field_end), + {P23, #protocol_field_begin{ type = ?tType_I8, id = 42 }} = read(P22, field_begin), + {P24, {ok, 8}} = read(P23, byte), + {P25, ok} = read(P24, field_end), + {P26, #protocol_field_begin{ type = ?tType_STOP}} = read(P25, field_begin), + {P27, ok} = read(P26, struct_end), + {P28, ok} = read(P27, message_end), + {P28, ok}. + +bool_field_test() -> + {ok, P0} = new(), + {P1, ok} = write(P0, #protocol_message_begin{ name = "Message1", type = ?tType_I8, seqid = 3}), + {P2, ok} = write(P1, #protocol_struct_begin{}), + {P3, ok} = write(P2, #protocol_field_begin{ name = "field1", type = ?tType_BOOL, id = 1}), + {P4, ok} = write(P3, {bool, true}), + {P5, ok} = write(P4, field_end), + {P6, ok} = write(P5, #protocol_field_begin{ name = "field2", type = ?tType_BOOL, id = 14}), + {P7, ok} = write(P6, {bool, false}), + {P8, ok} = write(P7, field_end), + {P9, ok} = write(P8, #protocol_field_begin{ name = "field3", type = ?tType_BOOL, id = 42}), + {P10, ok} = write(P9, {bool, true}), + {P11, ok} = write(P10, field_end), + {P12, ok} = write(P11, field_stop), + {P13, ok} = write(P12, struct_end), + {P14, ok} = write(P13, message_end), + + {P15, #protocol_message_begin{ name = "Message1", type = ?tType_I8, seqid = 3}} = read(P14, message_begin), + {P16, ok} = read(P15, struct_begin), + {P17, #protocol_field_begin{ type = ?tType_BOOL, id = 1 }} = read(P16, field_begin), + {P18, {ok, true}} = read(P17, bool), + {P19, ok} = read(P18, field_end), + {P20, #protocol_field_begin{ type = ?tType_BOOL, id = 14 }} = read(P19, field_begin), + {P21, {ok, false}} = read(P20, bool), + {P22, ok} = read(P21, field_end), + {P23, #protocol_field_begin{ type = ?tType_BOOL, id = 42 }} = read(P22, field_begin), + {P24, {ok, true}} = read(P23, bool), + {P25, ok} = read(P24, field_end), + {P26, #protocol_field_begin{ type = ?tType_STOP}} = read(P25, field_begin), + {P27, ok} = read(P26, struct_end), + {P28, ok} = read(P27, message_end), + {P28, ok}. + +nesting_test() -> + {ok, P0} = new(), + {P1, ok} = write(P0, #protocol_message_begin{ name = "Message1", type = ?tType_I8, seqid = 3}), + {P2, ok} = write(P1, #protocol_struct_begin{}), + {P3, ok} = write(P2, #protocol_field_begin{ name = "field1", type = ?tType_BOOL, id = 14}), + {P4, ok} = write(P3, {bool, true}), + {P5, ok} = write(P4, field_end), + + {P6, ok} = write(P5, #protocol_field_begin{ name = "field2", type = ?tType_STRUCT, id = 28}), + {P7, ok} = write(P6, #protocol_struct_begin{}), + {P8, ok} = write(P7, #protocol_field_begin{ name = "field2_1", type = ?tType_BOOL, id = 30000}), + {P9, ok} = write(P8, {bool, false}), + {P10, ok} = write(P9, field_end), + {P11, ok} = write(P10, field_stop), + {P12, ok} = write(P11, struct_end), + {P13, ok} = write(P12, field_end), + + {P14, ok} = write(P13, #protocol_field_begin{ name = "field3", type = ?tType_BOOL, id = 42}), + {P15, ok} = write(P14, {bool, true}), + {P16, ok} = write(P15, field_end), + {P17, ok} = write(P16, field_stop), + {P18, ok} = write(P17, struct_end), + {P19, ok} = write(P18, message_end), + + {P20, #protocol_message_begin{ name = "Message1", type = ?tType_I8, seqid = 3}} = read(P19, message_begin), + {P21, ok} = read(P20, struct_begin), + {P22, #protocol_field_begin{ type = ?tType_BOOL, id = 14 }} = read(P21, field_begin), + {P23, {ok, true}} = read(P22, bool), + {P24, ok} = read(P23, field_end), + + {P25, #protocol_field_begin{ type = ?tType_STRUCT, id = 28 }} = read(P24, field_begin), + {P26, ok} = read(P25, struct_begin), + {P27, #protocol_field_begin{ type = ?tType_BOOL, id = 30000 }} = read(P26, field_begin), + {P28, {ok, false}} = read(P27, bool), + {P29, ok} = read(P28, field_end), + {P30, #protocol_field_begin{ type = ?tType_STOP }} = read(P29, field_begin), + {P31, ok} = read(P30, struct_end), + {P32, ok} = read(P31, field_end), + + {P33, #protocol_field_begin{ type = ?tType_BOOL, id = 42 }} = read(P32, field_begin), + {P34, {ok, true}} = read(P33, bool), + {P35, ok} = read(P34, field_end), + {P36, #protocol_field_begin{ type = ?tType_STOP }} = read(P35, field_begin), + {P37, ok} = read(P36, struct_end), + {P38, ok} = read(P37, message_end), + {P38, ok}. diff --git a/src/jaegertracing/thrift/lib/erl/test/test_thrift_file_transport.erl b/src/jaegertracing/thrift/lib/erl/test/test_thrift_file_transport.erl new file mode 100644 index 000000000..3e5c1d1e4 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/test_thrift_file_transport.erl @@ -0,0 +1,213 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(test_thrift_file_transport). +-include_lib("eunit/include/eunit.hrl"). + + +new(File) -> thrift_file_transport:new(File). +new(File, Opts) -> thrift_file_transport:new(File, Opts). + +new_test_() -> + [ + {"new file", ?_assertMatch( + {ok, {_, thrift_file_transport, {t_file, a_fake_file, true, write}}}, + new(a_fake_file) + )}, + {"new file in read mode", ?_assertMatch( + {ok, {_, thrift_file_transport, {t_file, a_fake_file, true, read}}}, + new(a_fake_file, [{mode, read}]) + )}, + {"new file in write mode", ?_assertMatch( + {ok, {_, thrift_file_transport, {t_file, a_fake_file, true, write}}}, + new(a_fake_file, [{mode, write}]) + )}, + {"new file in should_close true mode", ?_assertMatch( + {ok, {_, thrift_file_transport, {t_file, a_fake_file, true, write}}}, + new(a_fake_file, [{should_close, true}]) + )}, + {"new file in should_close false mode", ?_assertMatch( + {ok, {_, thrift_file_transport, {t_file, a_fake_file, false, write}}}, + new(a_fake_file, [{should_close, false}]) + )} + ]. + + +read(File, Bytes) -> thrift_file_transport:read(File, Bytes). + +read_test_() -> + {setup, + fun() -> + meck:new(file, [unstick, passthrough]), + meck:expect(file, read, fun(Bin, N) -> + {Result, _} = split_binary(Bin, min(iolist_size(Bin), N)), + {ok, Result} + end) + end, + fun(_) -> meck:unload(file) end, + [ + {"read zero bytes from empty file", ?_assertMatch( + {_, {ok, <<>>}}, + read({t_file, <<>>, true, read}, 0) + )}, + {"read 1 byte from empty file", ?_assertMatch( + {_, {ok, <<>>}}, + read({t_file, <<>>, true, read}, 1) + )}, + {"read zero bytes from nonempty file", ?_assertMatch( + {_, {ok, <<>>}}, + read({t_file, <<"hallo world">>, true, read}, 0) + )}, + {"read 1 byte from nonempty file", ?_assertMatch( + {_, {ok, <<"h">>}}, + read({t_file, <<"hallo world">>, true, read}, 1) + )}, + {"read a zillion bytes from nonempty file", ?_assertMatch( + {_, {ok, <<"hallo world">>}}, + read({t_file, <<"hallo world">>, true, read}, 65536) + )}, + {"read 0 byte from file in write mode", ?_assertMatch( + {_, {error, write_mode}}, + read({t_file, <<>>, true, write}, 0) + )}, + {"read 1 byte from file in write mode", ?_assertMatch( + {_, {error, write_mode}}, + read({t_file, <<>>, true, write}, 1) + )} + ] + }. + + +read_exact(File, Bytes) -> thrift_file_transport:read_exact(File, Bytes). + +read_exact_test_() -> + {setup, + fun() -> + meck:new(file, [unstick, passthrough]), + meck:expect(file, read, fun(Bin, N) -> + {Result, _} = split_binary(Bin, min(iolist_size(Bin), N)), + {ok, Result} + end) + end, + fun(_) -> meck:unload(file) end, + [ + {"read exactly zero bytes from empty file", ?_assertMatch( + {_, {ok, <<>>}}, + read_exact({t_file, <<>>, true, read}, 0) + )}, + {"read exactly 1 byte from empty file", ?_assertMatch( + {_, {error, eof}}, + read_exact({t_file, <<>>, true, read}, 1) + )}, + {"read exactly zero bytes from nonempty file", ?_assertMatch( + {_, {ok, <<>>}}, + read_exact({t_file, <<"hallo world">>, true, read}, 0) + )}, + {"read exactly 1 byte from nonempty file", ?_assertMatch( + {_, {ok, <<"h">>}}, + read_exact({t_file, <<"hallo world">>, true, read}, 1) + )}, + {"read exactly a zillion bytes from nonempty file", ?_assertMatch( + {_, {error, eof}}, + read_exact({t_file, <<"hallo world">>, true, read}, 65536) + )}, + {"read exactly 0 byte from file in write mode", ?_assertMatch( + {_, {error, write_mode}}, + read_exact({t_file, <<>>, true, write}, 0) + )}, + {"read exactly 1 byte from file in write mode", ?_assertMatch( + {_, {error, write_mode}}, + read_exact({t_file, <<>>, true, write}, 1) + )} + ] + }. + + +write(File, Data) -> thrift_file_transport:write(File, Data). + +write_test_() -> + {setup, + fun() -> + meck:new(file, [unstick, passthrough]), + meck:expect(file, write, fun(_, _) -> ok end) + end, + fun(_) -> meck:unload(file) end, + [ + {"write empty list to file", ?_assertMatch( + {{t_file, a_fake_file, true, write}, ok}, + write({t_file, a_fake_file, true, write}, []) + )}, + {"write empty binary to file", ?_assertMatch( + {{t_file, a_fake_file, true, write}, ok}, + write({t_file, a_fake_file, true, write}, <<>>) + )}, + {"write a list to file", ?_assertMatch( + {{t_file, a_fake_file, true, write}, ok}, + write({t_file, a_fake_file, true, write}, "hallo world") + )}, + {"write a binary to file", ?_assertMatch( + {{t_file, a_fake_file, true, write}, ok}, + write({t_file, a_fake_file, true, write}, <<"hallo world">>) + )}, + {"write a binary to file in read mode", ?_assertMatch( + {_, {error, read_mode}}, + write({t_file, a_fake_file, true, read}, <<"hallo world">>) + )}, + {"write a list to file in read mode", ?_assertMatch( + {_, {error, read_mode}}, + write({t_file, a_fake_file, true, read}, "hallo world") + )} + ] + }. + + +flush(Transport) -> thrift_file_transport:flush(Transport). + +flush_test_() -> + {setup, + fun() -> + meck:new(file, [unstick, passthrough]), + meck:expect(file, sync, fun(_File) -> ok end) + end, + fun(_) -> meck:unload(file) end, + [ + {"flush file", ?_assertMatch( + {{t_file, a_fake_file, true, write}, ok}, + flush({t_file, a_fake_file, true, write}) + )} + ] + }. + + +close(Transport) -> thrift_file_transport:close(Transport). + +close_test_() -> + {setup, + fun() -> + meck:new(file, [unstick, passthrough]), + meck:expect(file, close, fun(_) -> ok end) + end, + fun(_) -> meck:unload(file) end, + [ + {"close file", ?_assertMatch( + {{t_file, a_fake_file, true, write}, ok}, + close({t_file, a_fake_file, true, write}) + )} + ] + }. \ No newline at end of file diff --git a/src/jaegertracing/thrift/lib/erl/test/test_thrift_framed_transport.erl b/src/jaegertracing/thrift/lib/erl/test/test_thrift_framed_transport.erl new file mode 100644 index 000000000..8a538a53a --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/test_thrift_framed_transport.erl @@ -0,0 +1,404 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(test_thrift_framed_transport). +-include_lib("eunit/include/eunit.hrl"). + + +new(Transport) -> thrift_framed_transport:new(Transport). + +new_test_() -> + [ + {"new framed membuffer", ?_assertMatch( + {ok, {t_transport, thrift_framed_transport, {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, []}}, + [], + [] + }}}, + new({t_transport, thrift_membuffer_transport, {t_membuffer, []}}) + )} + ]. + + +read(Frame, Bytes) -> thrift_framed_transport:read(Frame, Bytes). + +read_test_() -> + [ + {"read zero bytes from an empty framed membuffer", ?_assertMatch( + { + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [], + [] + }, + {ok, <<>>} + }, + read( + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [], + [] + }, + 0 + ) + )}, + {"read 1 byte from an empty framed membuffer", ?_assertMatch( + {_, {error, eof}}, + read( + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [], + [] + }, + 1 + ) + )}, + {"read zero bytes from nonempty framed membuffer", ?_assertMatch( + { + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, + <<0, 0, 0, 11, "hallo world">> + }}, + [], + [] + }, + {ok, <<>>} + }, + read( + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, + <<0, 0, 0, 11, "hallo world">> + }}, + [], + [] + }, + 0 + ) + )}, + {"read 1 byte from nonempty framed membuffer", ?_assertMatch( + { + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + <<"allo world">>, + [] + }, + {ok, <<"h">>} + }, + read( + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, + <<0, 0, 0, 11, "hallo world">> + }}, + [], + [] + }, + 1 + ) + )}, + {"read 1 byte from nonempty buffer", ?_assertMatch( + { + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + <<"allo world">>, + [] + }, + {ok, <<"h">>} + }, + read( + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + <<"hallo world">>, + [] + }, + 1 + ) + )}, + {"read a zillion bytes from nonempty framed membuffer", ?_assertMatch( + { + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + <<>>, + [] + }, + {ok, <<"hallo world">>} + }, + read( + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, + <<0, 0, 0, 11, "hallo world">> + }}, + [], + [] + }, + 65536 + ) + )} + ]. + + +read_exact(Frame, Bytes) -> thrift_framed_transport:read_exact(Frame, Bytes). + +read_exact_test_() -> + [ + {"read exactly zero bytes from an empty framed membuffer", ?_assertMatch( + { + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + <<>>, + [] + }, + {ok, <<>>} + }, + read_exact( + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [], + [] + }, + 0 + ) + )}, + {"read exactly 1 byte from an empty framed membuffer", ?_assertMatch( + {_, {error, eof}}, + read_exact( + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [], + [] + }, + 1 + ) + )}, + {"read exactly zero bytes from nonempty framed membuffer", ?_assertMatch( + { + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, + <<0, 0, 0, 11, "hallo world">> + }}, + <<>>, + [] + }, + {ok, <<>>} + }, + read_exact( + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, + <<0, 0, 0, 11, "hallo world">> + }}, + [], + [] + }, + 0 + ) + )}, + {"read exactly 1 byte from nonempty framed membuffer", ?_assertMatch( + { + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + <<"allo world">>, + [] + }, + {ok, <<"h">>} + }, + read_exact( + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, + <<0, 0, 0, 11, "hallo world">> + }}, + [], + [] + }, + 1 + ) + )}, + {"read exactly 1 byte from nonempty buffer", ?_assertMatch( + { + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + <<"allo world">>, + [] + }, + {ok, <<"h">>} + }, + read_exact( + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + <<"hallo world">>, + [] + }, + 1 + ) + )}, + {"read exactly a zillion bytes from nonempty framed membuffer", ?_assertMatch( + { + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [[],<<"hallo world">>], + [] + }, + {error, eof} + }, + read_exact( + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, + <<0, 0, 0, 11, "hallo world">> + }}, + [], + [] + }, + 65536 + ) + )} + ]. + + +write(Framed, Data) -> thrift_framed_transport:write(Framed, Data). + +write_test_() -> + [ + {"write empty list to empty framed membuffer", ?_assertMatch( + { + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [], + [[], []] + }, + ok + }, + write( + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [], + [] + }, + [] + ) + )}, + {"write empty list to nonempty framed membuffer", ?_assertMatch( + { + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [], + [["hallo world"], []] + }, + ok + }, + write( + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [], + ["hallo world"] + }, + [] + ) + )}, + {"write empty binary to empty framed membuffer", ?_assertMatch( + { + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [], + [[], <<>>] + }, + ok + }, + write( + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [], + [] + }, + <<>> + ) + )}, + {"write empty binary to nonempty framed membuffer", ?_assertMatch( + { + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [], + [["hallo world"], <<>>] + }, + ok + }, + write( + {t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [], + ["hallo world"] + }, + <<>> + ) + )} + ]. + + +flush(Transport) -> thrift_framed_transport:flush(Transport). + +flush_test_() -> + [ + {"flush empty framed membuffer", ?_assertMatch( + {{t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [], + [] + }, + ok + }, + flush({t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [], + [] + }) + )}, + {"flush nonempty framed membuffer", ?_assertMatch( + {{t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, + [<<>>, [<<0, 0, 0, 11>>, <<"hallo world">>]] + }}, + [], + [] + }, + ok + }, + flush({t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [], + <<"hallo world">> + }) + )} + ]. + + +close(Transport) -> thrift_framed_transport:close(Transport). + +close_test_() -> + {"close framed membuffer", ?_assertMatch( + {{t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [], + [] + }, + ok + }, + close({t_framed, + {t_transport, thrift_membuffer_transport, {t_membuffer, <<>>}}, + [], + [] + }) + )}. + diff --git a/src/jaegertracing/thrift/lib/erl/test/test_thrift_membuffer_transport.erl b/src/jaegertracing/thrift/lib/erl/test/test_thrift_membuffer_transport.erl new file mode 100644 index 000000000..9689c7987 --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/test_thrift_membuffer_transport.erl @@ -0,0 +1,167 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(test_thrift_membuffer_transport). +-include_lib("eunit/include/eunit.hrl"). + + +new() -> thrift_membuffer_transport:new(). +new(Data) -> thrift_membuffer_transport:new(Data). + +new_test_() -> + [ + {"new empty membuffer", ?_assertMatch( + {ok, {_, _, {t_membuffer, []}}}, + new() + )}, + {"new membuffer with <<>>", ?_assertMatch( + {ok, {_, _, {t_membuffer, [<<>>]}}}, + new(<<>>) + )}, + {"new membuffer with []", ?_assertMatch( + {ok, {_, _, {t_membuffer, []}}}, + new([]) + )}, + {"new membuffer with <<\"hallo world\">>", ?_assertMatch( + {ok, {_, _, {t_membuffer, [<<"hallo world">>]}}}, + new(<<"hallo world">>) + )}, + {"new membuffer with \"hallo world\"", ?_assertMatch( + {ok, {_, _, {t_membuffer, "hallo world"}}}, + new("hallo world") + )} + ]. + + +read(Membuffer, Bytes) -> thrift_membuffer_transport:read(Membuffer, Bytes). + +read_test_() -> + [ + {"read zero bytes from an empty membuffer", ?_assertMatch( + {_, {ok, <<>>}}, + read({t_membuffer, []}, 0) + )}, + {"read 1 byte from an empty membuffer", ?_assertMatch( + {_, {ok, <<>>}}, + read({t_membuffer, []}, 1) + )}, + {"read zero bytes from nonempty membuffer", ?_assertMatch( + {{t_membuffer, <<"hallo world">>}, {ok, <<>>}}, + read({t_membuffer, [["hallo", " "], "world"]}, 0) + )}, + {"read 1 byte from nonempty membuffer", ?_assertMatch( + {{t_membuffer, <<"allo world">>}, {ok, <<"h">>}}, + read({t_membuffer, [["hallo", " "], "world"]}, 1) + )}, + {"read a zillion bytes from nonempty buffer", ?_assertMatch( + {{t_membuffer, <<>>}, {ok, <<"hallo world">>}}, + read({t_membuffer, [["hallo", " "], "world"]}, 65536) + )} + ]. + + +read_exact(Membuffer, Bytes) -> + thrift_membuffer_transport:read_exact(Membuffer, Bytes). + +read_exact_test_() -> + [ + {"read exactly zero bytes from an empty membuffer", ?_assertMatch( + {_, {ok, <<>>}}, + read_exact({t_membuffer, []}, 0) + )}, + {"read exactly 1 byte from an empty membuffer", ?_assertMatch( + {_, {error, eof}}, + read_exact({t_membuffer, []}, 1) + )}, + {"read exactly zero bytes from nonempty membuffer", ?_assertMatch( + {{t_membuffer, <<"hallo world">>}, {ok, <<>>}}, + read_exact({t_membuffer, [["hallo", " "], "world"]}, 0) + )}, + {"read exactly 1 byte from nonempty membuffer", ?_assertMatch( + {{t_membuffer, <<"allo world">>}, {ok, <<"h">>}}, + read_exact({t_membuffer, [["hallo", " "], "world"]}, 1) + )}, + {"read exactly a zillion bytes from nonempty buffer", ?_assertMatch( + {{t_membuffer, [["hallo", " "], "world"]}, {error, eof}}, + read_exact({t_membuffer, [["hallo", " "], "world"]}, 65536) + )} + ]. + + +write(Membuffer, Data) -> thrift_membuffer_transport:write(Membuffer, Data). + +write_test_() -> + [ + {"write empty list to empty membuffer", ?_assertMatch( + {{t_membuffer, [[], []]}, ok}, + write({t_membuffer, []}, []) + )}, + {"write empty list to nonempty membuffer", ?_assertMatch( + {{t_membuffer, ["hallo world", []]}, ok}, + write({t_membuffer, "hallo world"}, []) + )}, + {"write empty binary to empty membuffer", ?_assertMatch( + {{t_membuffer, [[], <<>>]}, ok}, + write({t_membuffer, []}, <<>>) + )}, + {"write empty binary to nonempty membuffer", ?_assertMatch( + {{t_membuffer, ["hallo world", <<>>]}, ok}, + write({t_membuffer, "hallo world"}, <<>>) + )}, + {"write a list to empty membuffer", ?_assertMatch( + {{t_membuffer, [[], "hallo world"]}, ok}, + write({t_membuffer, []}, "hallo world") + )}, + {"write a list to nonempty membuffer", ?_assertMatch( + {{t_membuffer, [["hallo", " "], "world"]}, ok}, + write({t_membuffer, ["hallo", " "]}, "world") + )}, + {"write a binary to empty membuffer", ?_assertMatch( + {{t_membuffer, [[], <<"hallo world">>]}, ok}, + write({t_membuffer, []}, <<"hallo world">>) + )}, + {"write a binary to nonempty membuffer", ?_assertMatch( + {{t_membuffer, [["hallo", " "], <<"world">>]}, ok}, + write({t_membuffer, ["hallo", " "]}, <<"world">>) + )} + ]. + + +flush(Transport) -> thrift_membuffer_transport:flush(Transport). + +flush_test_() -> + [ + {"flush empty membuffer", ?_assertMatch( + {{t_membuffer, []}, ok}, + flush({t_membuffer, []}) + )}, + {"flush nonempty membuffer", ?_assertMatch( + {{t_membuffer, [<<"hallo world">>]}, ok}, + flush({t_membuffer, [<<"hallo world">>]}) + )} + ]. + + +close(Transport) -> thrift_membuffer_transport:close(Transport). + +close_test_() -> + {"close membuffer", ?_assertMatch( + {{t_membuffer, _}, ok}, + close({t_membuffer, []}) + )}. \ No newline at end of file diff --git a/src/jaegertracing/thrift/lib/erl/test/test_thrift_socket_transport.erl b/src/jaegertracing/thrift/lib/erl/test/test_thrift_socket_transport.erl new file mode 100644 index 000000000..5bc0f246d --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/test_thrift_socket_transport.erl @@ -0,0 +1,199 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(test_thrift_socket_transport). +-include_lib("eunit/include/eunit.hrl"). + + +new(Socket) -> thrift_socket_transport:new(Socket). +new(Socket, Opts) -> thrift_socket_transport:new(Socket, Opts). + +new_test_() -> + [ + {"new socket", ?_assertMatch( + {ok, {_, thrift_socket_transport, {t_socket, a_fake_socket, 60000, []}}}, + new(a_fake_socket) + )}, + {"new socket with no options", ?_assertMatch( + {ok, {_, thrift_socket_transport, {t_socket, a_fake_socket, 60000, []}}}, + new(a_fake_socket, []) + )}, + {"new socket with integer timeout", ?_assertMatch( + {ok, {_, thrift_socket_transport, {t_socket, a_fake_socket, 5000, []}}}, + new(a_fake_socket, [{recv_timeout, 5000}]) + )}, + {"new socket with infinity timeout", ?_assertMatch( + {ok, {_, thrift_socket_transport, {t_socket, a_fake_socket, infinity, []}}}, + new(a_fake_socket, [{recv_timeout, infinity}]) + )} + ]. + + +read(Socket, Bytes) -> thrift_socket_transport:read(Socket, Bytes). + +read_test_() -> + {setup, + fun() -> + meck:new(gen_tcp, [unstick, passthrough]), + meck:expect(gen_tcp, recv, fun(Bin, 0, _) -> {ok, Bin} end) + end, + fun(_) -> meck:unload(gen_tcp) end, + [ + {"read zero bytes from empty socket", ?_assertMatch( + {_, {ok, <<>>}}, + read({t_socket, <<>>, 60000, []}, 0) + )}, + {"read 1 byte from empty socket", ?_assertMatch( + {_, {ok, <<>>}}, + read({t_socket, <<>>, 60000, []}, 1) + )}, + {"read zero bytes from nonempty socket", ?_assertMatch( + {{t_socket, _, _, _}, {ok, <<>>}}, + read({t_socket, <<"hallo world">>, 60000, []}, 0) + )}, + {"read 1 byte from nonempty socket", ?_assertMatch( + {{t_socket, _, _, <<"allo world">>}, {ok, <<"h">>}}, + read({t_socket, <<"hallo world">>, 60000, []}, 1) + )}, + {"read a zillion bytes from nonempty socket", ?_assertMatch( + {{t_socket, _, _, <<>>}, {ok, <<"hallo world">>}}, + read({t_socket, <<"hallo world">>, 60000, []}, 65536) + )}, + {"read 1 byte from previously buffered socket", ?_assertMatch( + {{t_socket, _, _, <<"allo">>}, {ok, <<"h">>}}, + read({t_socket, <<" world">>, 60000, <<"hallo">>}, 1) + )}, + {"read 6 byte from previously buffered socket", ?_assertMatch( + {{t_socket, _, _, <<"world">>}, {ok, <<"hallo ">>}}, + read({t_socket, <<" world">>, 60000, <<"hallo">>}, 6) + )}, + {"read a zillion bytes from previously buffered socket", ?_assertMatch( + {{t_socket, _, _, <<>>}, {ok, <<"hallo world">>}}, + read({t_socket, <<" world">>, 60000, <<"hallo">>}, 65536) + )} + ] + }. + + +read_exact(Socket, Bytes) -> thrift_socket_transport:read_exact(Socket, Bytes). + +read_exact_test_() -> + {setup, + fun() -> + meck:new(gen_tcp, [unstick, passthrough]), + meck:expect(gen_tcp, recv, fun(Bin, N, _) -> + case N of + 0 -> {ok, Bin}; + 1 -> {ok, <<"h">>}; + N when N > 2 -> {error, timeout} + end + end), + meck:expect(gen_tcp, close, fun(_) -> ok end) + end, + fun(_) -> meck:unload(gen_tcp) end, + [ + {"read_exact zero bytes from empty socket", ?_assertMatch( + {_, {ok, <<>>}}, + read_exact({t_socket, <<>>, 60000, []}, 0) + )}, + {"read_exact zero bytes from nonempty socket", ?_assertMatch( + {{t_socket, _, _, _}, {ok, <<>>}}, + read_exact({t_socket, <<"hallo world">>, 60000, []}, 0) + )}, + {"read_exact 1 byte from nonempty socket", ?_assertMatch( + {{t_socket, _, _, []}, {ok, <<"h">>}}, + read_exact({t_socket, <<"hallo world">>, 60000, []}, 1) + )}, + {"read_exact a zillion bytes from nonempty socket", ?_assertMatch( + {{t_socket, _, _, []}, {error, timeout}}, + read_exact({t_socket, <<"hallo world">>, 60000, []}, 65536) + )}, + {"read_exact 1 byte from previously buffered socket", ?_assertMatch( + {{t_socket, _, _, <<"allo">>}, {ok, <<"h">>}}, + read_exact({t_socket, <<" world">>, 60000, <<"hallo">>}, 1) + )}, + {"read_exact 6 byte from previously buffered socket", ?_assertMatch( + {{t_socket, _, _, []}, {ok, <<"more h">>}}, + read_exact({t_socket, <<"hallo">>, 60000, <<"more ">>}, 6) + )}, + {"read_exact a zillion bytes from previously buffered socket", ?_assertMatch( + {{t_socket, _, _, <<"hallo">>}, {error, timeout}}, + read_exact({t_socket, <<" world">>, 60000, <<"hallo">>}, 65536) + )} + ] + }. + + +write(Socket, Data) -> thrift_socket_transport:write(Socket, Data). + +write_test_() -> + {setup, + fun() -> + meck:new(gen_tcp, [unstick, passthrough]), + meck:expect(gen_tcp, send, fun(_, _) -> ok end) + end, + fun(_) -> meck:unload(gen_tcp) end, + [ + {"write empty list to socket", ?_assertMatch( + {{t_socket, a_fake_socket, 60000, []}, ok}, + write({t_socket, a_fake_socket, 60000, []}, []) + )}, + {"write empty binary to socket", ?_assertMatch( + {{t_socket, a_fake_socket, 60000, []}, ok}, + write({t_socket, a_fake_socket, 60000, []}, <<>>) + )}, + {"write a list to socket", ?_assertMatch( + {{t_socket, a_fake_socket, 60000, []}, ok}, + write({t_socket, a_fake_socket, 60000, []}, "hallo world") + )}, + {"write a binary to socket", ?_assertMatch( + {{t_socket, a_fake_socket, 60000, []}, ok}, + write({t_socket, a_fake_socket, 60000, []}, <<"hallo world">>) + )} + ] + }. + + +flush(Transport) -> thrift_socket_transport:flush(Transport). + +flush_test_() -> + [ + {"flush socket", ?_assertMatch( + {{t_socket, a_fake_socket, 60000, []}, ok}, + flush({t_socket, a_fake_socket, 60000, []}) + )} + ]. + + +close(Transport) -> thrift_socket_transport:close(Transport). + +close_test_() -> + {setup, + fun() -> + meck:new(gen_tcp, [unstick, passthrough]), + meck:expect(gen_tcp, close, fun(_) -> ok end) + end, + fun(_) -> meck:unload(gen_tcp) end, + [ + {"close membuffer", ?_assertMatch( + {{t_socket, a_fake_socket, 60000, []}, ok}, + close({t_socket, a_fake_socket, 60000, []}) + )} + ] + }. \ No newline at end of file diff --git a/src/jaegertracing/thrift/lib/erl/test/thrift_socket_server_test.erl b/src/jaegertracing/thrift/lib/erl/test/thrift_socket_server_test.erl new file mode 100644 index 000000000..0818b844e --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/thrift_socket_server_test.erl @@ -0,0 +1,49 @@ +-module (thrift_socket_server_test). + +-include_lib("eunit/include/eunit.hrl"). + +-include ("thrift_constants.hrl"). + +parse_handler_options_test_() -> + CorrectServiceHandlerOptionList = [{?MULTIPLEXED_ERROR_HANDLER_KEY, ?MODULE}, {"Service1", ?MODULE}, {"Service2", ?MODULE}], + MissingErrorHandlerOptionList = [{"Service1", ?MODULE}, {"Service2", ?MODULE}], + WrongService2HandlerOptionList = [{?MULTIPLEXED_ERROR_HANDLER_KEY, ?MODULE}, {"Service1", ?MODULE}, {"Service2", "Module"}], + WrongServiceKeyOptionList = [{?MULTIPLEXED_ERROR_HANDLER_KEY, ?MODULE}, {'service1', ?MODULE}, {"Service2", ?MODULE}], + CorrectHandlerTestFunction = fun() -> + ?assertMatch({thrift_socket_server,_,_,_,_,_,_,_,_,_,_,_,_,_}, thrift_socket_server:parse_options([{handler, CorrectServiceHandlerOptionList}])), + {thrift_socket_server,_,_, HandlerList,_,_,_,_,_,_,_,_,_,_} = thrift_socket_server:parse_options([{handler, CorrectServiceHandlerOptionList}]), + lists:foreach(fun + ({ServiceName, HandlerModule}) -> + ?assertMatch({ok, HandlerModule} when is_atom(HandlerModule), thrift_multiplexed_map_wrapper:find(ServiceName, HandlerList)) + end, CorrectServiceHandlerOptionList) + end, + [ + {"Bad argument for the handler option", ?_assertThrow(_, thrift_socket_server:parse_options([{handler, []}]))}, + {"Try to parse the handler option twice", ?_assertThrow(_, thrift_socket_server:parse_options([{handler, ?MODULE}, {handler, CorrectServiceHandlerOptionList}]))}, + {"Parse the handler option as a non multiplexed service handler", ?_assertMatch({thrift_socket_server,_,_,?MODULE,_,_,_,_,_,_,_,_,_,_}, thrift_socket_server:parse_options([{handler, ?MODULE}]))}, + {"No error handler was defined", ?_assertThrow(_, thrift_socket_server:parse_options([{handler, MissingErrorHandlerOptionList}]))}, + {"Bad handler module for Service2", ?_assertThrow(_, thrift_socket_server:parse_options([{handler, WrongService2HandlerOptionList}]))}, + {"Bad service key for Service1", ?_assertThrow(_, thrift_socket_server:parse_options([{handler, WrongServiceKeyOptionList}]))}, + {"Try to parse a correct handler option list", CorrectHandlerTestFunction} + ]. + +parse_service_options_test_() -> + CorrectServiceModuleOptionList = [{"Service1", ?MODULE}, {"Service2", ?MODULE}], + WrongService2ModuleOptionList = [{"Service1", ?MODULE}, {"Service2", "thrift_service_module"}], + WrongServiceKeyOptionList = [{'service1', ?MODULE}, {"Service2", ?MODULE}], + CorrectServiceModuleTestFunction = fun() -> + ?assertMatch({thrift_socket_server,_,_,_,_,_,_,_,_,_,_,_,_,_}, thrift_socket_server:parse_options([{service, CorrectServiceModuleOptionList}])), + {thrift_socket_server,_, ServiceModuleList,_,_,_,_,_,_,_,_,_,_,_} = thrift_socket_server:parse_options([{service, CorrectServiceModuleOptionList}]), + lists:foreach(fun + ({ServiceName, ServiceModule}) -> + ?assertMatch({ok, ServiceModule} when is_atom(ServiceModule), thrift_multiplexed_map_wrapper:find(ServiceName, ServiceModuleList)) + end, CorrectServiceModuleOptionList) + end, + [ + {"Bad argument for the service option", ?_assertThrow(_, thrift_socket_server:parse_options([{service, []}]))}, + {"Try to parse the service option twice", ?_assertThrow(_, thrift_socket_server:parse_options([{service, ?MODULE}, {service, CorrectServiceModuleOptionList}]))}, + {"Parse a service module for a non multiplexed service", ?_assertMatch({thrift_socket_server,_,?MODULE,_,_,_,_,_,_,_,_,_,_,_}, thrift_socket_server:parse_options([{service, ?MODULE}]))}, + {"Bad service module for Service2", ?_assertThrow(_, thrift_socket_server:parse_options([{service, WrongService2ModuleOptionList}]))}, + {"Bad service key for Service1", ?_assertThrow(_, thrift_socket_server:parse_options([{service, WrongServiceKeyOptionList}]))}, + {"Try to parse a correct service option list", CorrectServiceModuleTestFunction} + ]. diff --git a/src/jaegertracing/thrift/lib/erl/test/thrift_test_test.erl b/src/jaegertracing/thrift/lib/erl/test/thrift_test_test.erl new file mode 100644 index 000000000..77df61d9a --- /dev/null +++ b/src/jaegertracing/thrift/lib/erl/test/thrift_test_test.erl @@ -0,0 +1,643 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +% don't rename this thrift_test, it clobbers generated files +-module(thrift_test_test). +-compile(export_all). + +-include_lib("eunit/include/eunit.hrl"). + +-include("gen-erl/thrift_test_constants.hrl"). + +constant_test_() -> + [ + {"myNumberz equals 1", ?_assertEqual(1, ?THRIFT_TEST_MYNUMBERZ)} + ]. + +record_generation_test_() -> + [ + {"Bonk record", ?_assertMatch( + {'thrift.test.Bonk', _, _}, + #'thrift.test.Bonk'{message=null,type=null} + )}, + {"Bools record", ?_assertMatch( + {'thrift.test.Bools', _, _}, + #'thrift.test.Bools'{im_true=null,im_false=null} + )}, + {"Xtruct record", ?_assertMatch( + {'thrift.test.Xtruct', _, _, _, _}, + #'thrift.test.Xtruct'{string_thing=null,byte_thing=null,i32_thing=null,i64_thing=null} + )}, + {"Xtruct2 record", ?_assertMatch( + {'thrift.test.Xtruct2', _, _, _}, + #'thrift.test.Xtruct2'{byte_thing=null,struct_thing=null,i32_thing=null} + )}, + {"Xtruct3 record", ?_assertMatch( + {'thrift.test.Xtruct3', _, _, _, _}, + #'thrift.test.Xtruct3'{string_thing=null,changed=null,i32_thing=null,i64_thing=null} + )}, + {"Insanity record", ?_assertMatch( + {'thrift.test.Insanity', _, _}, + #'thrift.test.Insanity'{userMap=null,xtructs=null} + )}, + {"CrazyNesting record", ?_assertMatch( + {'thrift.test.CrazyNesting', _, _, _, _}, + #'thrift.test.CrazyNesting'{ + string_field=null, + set_field=null, + list_field=null, + binary_field=null + } + )}, + {"Xception record", ?_assertMatch( + {'thrift.test.Xception', _, _}, + #'thrift.test.Xception'{errorCode=null,message=null} + )}, + {"Xception2 record", ?_assertMatch( + {'thrift.test.Xception2', _, _}, + #'thrift.test.Xception2'{errorCode=null,struct_thing=null} + )}, + {"EmptyStruct record", ?_assertMatch({'thrift.test.EmptyStruct'}, #'thrift.test.EmptyStruct'{})}, + {"OneField record", ?_assertMatch({'thrift.test.OneField', _}, #'thrift.test.OneField'{field=null})}, + {"VersioningTestV1 record", ?_assertMatch( + {'thrift.test.VersioningTestV1', _, _, _}, + #'thrift.test.VersioningTestV1'{begin_in_both=null,old_string=null,end_in_both=null} + )}, + {"VersioningTestV2 record", ?_assertMatch( + {'thrift.test.VersioningTestV2', _, _, _, _, _, _, _, _, _, _, _, _}, + #'thrift.test.VersioningTestV2'{ + begin_in_both=null, + newint=null, + newbyte=null, + newshort=null, + newlong=null, + newdouble=null, + newstruct=null, + newlist=null, + newset=null, + newmap=null, + newstring=null, + end_in_both=null + } + )}, + {"ListTypeVersioningV1 record", ?_assertMatch( + {'thrift.test.ListTypeVersioningV1', _, _}, + #'thrift.test.ListTypeVersioningV1'{myints=null,hello=null} + )}, + {"ListTypeVersioningV2 record", ?_assertMatch( + {'thrift.test.ListTypeVersioningV2', _, _}, + #'thrift.test.ListTypeVersioningV2'{strings=null,hello=null} + )}, + {"GuessProtocolStruct record", ?_assertMatch( + {'thrift.test.GuessProtocolStruct', _}, + #'thrift.test.GuessProtocolStruct'{map_field=null} + )}, + {"LargeDeltas record", ?_assertMatch( + {'thrift.test.LargeDeltas', _, _, _, _, _, _, _, _, _, _}, + #'thrift.test.LargeDeltas'{ + b1=null, + b10=null, + b100=null, + check_true=null, + b1000=null, + check_false=null, + vertwo2000=null, + a_set2500=null, + vertwo3000=null, + big_numbers=null + } + )}, + {"NestedListsI32x2 record", ?_assertMatch( + {'thrift.test.NestedListsI32x2', _}, + #'thrift.test.NestedListsI32x2'{integerlist=null} + )}, + {"NestedListsI32x3 record", ?_assertMatch( + {'thrift.test.NestedListsI32x3', _}, + #'thrift.test.NestedListsI32x3'{integerlist=null} + )}, + {"NestedMixedx2 record", ?_assertMatch( + {'thrift.test.NestedMixedx2', _, _, _}, + #'thrift.test.NestedMixedx2'{ + int_set_list=null, + map_int_strset=null, + map_int_strset_list=null + } + )}, + {"ListBonks record", ?_assertMatch({'thrift.test.ListBonks', _}, #'thrift.test.ListBonks'{bonk=null})}, + {"NestedListsBonk record", ?_assertMatch( + {'thrift.test.NestedListsBonk', _}, + #'thrift.test.NestedListsBonk'{bonk=null} + )}, + {"BoolTest record", ?_assertMatch( + {'thrift.test.BoolTest', _, _}, + #'thrift.test.BoolTest'{b=null,s=null} + )}, + {"StructA record", ?_assertMatch({'thrift.test.StructA', _}, #'thrift.test.StructA'{s=null})}, + {"StructB record", ?_assertMatch( + {'thrift.test.StructB', _, _}, + #'thrift.test.StructB'{aa=null,ab=null} + )} + ]. + +struct_info_test_() -> + [ + {"Bonk definition (short version)", ?_assertEqual( + {struct, [{1, string}, {2, i32}]}, + thrift_test_types:struct_info('thrift.test.Bonk') + )}, + {"Bonk definition", ?_assertEqual( + {struct, [ + {1, undefined, string, message, undefined}, + {2, undefined, i32, type, undefined} + ]}, + thrift_test_types:struct_info_ext('thrift.test.Bonk') + )}, + {"Bools definition", ?_assertEqual( + {struct, [ + {1, undefined, bool, im_true, undefined}, + {2, undefined, bool, im_false, undefined} + ]}, + thrift_test_types:struct_info_ext('thrift.test.Bools') + )}, + {"Xtruct definition", ?_assertEqual( + {struct, [ + {1, undefined, string, string_thing, undefined}, + {4, undefined, byte, byte_thing, undefined}, + {9, undefined, i32, i32_thing, undefined}, + {11, undefined, i64, i64_thing, undefined} + ]}, + thrift_test_types:struct_info_ext('thrift.test.Xtruct') + )}, + {"Xtruct2 definition", ?_assertEqual( + {struct, [ + {1, undefined, byte, byte_thing, undefined}, + {2, undefined, {struct, {'thrift_test_types', 'thrift.test.Xtruct'}}, struct_thing, #'thrift.test.Xtruct'{}}, + {3, undefined, i32, i32_thing, undefined} + ]}, + thrift_test_types:struct_info_ext('thrift.test.Xtruct2') + )}, + {"Xtruct3 definition", ?_assertEqual( + {struct, [ + {1, undefined, string, string_thing, undefined}, + {4, undefined, i32, changed, undefined}, + {9, undefined, i32, i32_thing, undefined}, + {11, undefined, i64, i64_thing, undefined} + ]}, + thrift_test_types:struct_info_ext('thrift.test.Xtruct3') + )}, + {"Insanity definition", ?_assertEqual( + {struct, [ + {1, undefined, {map, i32, i64}, userMap, dict:new()}, + {2, undefined, {list, {struct, {'thrift_test_types', 'thrift.test.Xtruct'}}}, xtructs, []} + ]}, + thrift_test_types:struct_info_ext('thrift.test.Insanity') + )}, + {"CrazyNesting definition", ?_assertEqual( + {struct, [ + {1, undefined, string, string_field, undefined}, + {2, optional, {set, {struct, {'thrift_test_types', 'thrift.test.Insanity'}}}, set_field, sets:new()}, + {3, required, {list, {map, + {set, i32}, + {map, i32, {set, {list, {map, {struct, {'thrift_test_types', 'thrift.test.Insanity'}}, string}}}} + }}, list_field, []}, + {4, undefined, string, binary_field, undefined} + ]}, + thrift_test_types:struct_info_ext('thrift.test.CrazyNesting') + )}, + {"Xception definition", ?_assertEqual( + {struct, [ + {1, undefined, i32, errorCode, undefined}, + {2, undefined, string, message, undefined} + ]}, + thrift_test_types:struct_info_ext('thrift.test.Xception') + )}, + {"Xception2 definition", ?_assertEqual( + {struct, [ + {1, undefined, i32, errorCode, undefined}, + {2, undefined, {struct, {'thrift_test_types', 'thrift.test.Xtruct'}}, struct_thing, #'thrift.test.Xtruct'{}} + ]}, + thrift_test_types:struct_info_ext('thrift.test.Xception2') + )}, + {"EmptyStruct definition", ?_assertEqual( + {struct, []}, + thrift_test_types:struct_info_ext('thrift.test.EmptyStruct') + )}, + {"OneField definition", ?_assertEqual( + {struct, [ + {1, undefined, {struct, {'thrift_test_types', 'thrift.test.EmptyStruct'}}, field, #'thrift.test.EmptyStruct'{}} + ]}, + thrift_test_types:struct_info_ext('thrift.test.OneField') + )}, + {"VersioningTestV1 definition", ?_assertEqual( + {struct, [ + {1, undefined, i32, begin_in_both, undefined}, + {3, undefined, string, old_string, undefined}, + {12, undefined, i32, end_in_both, undefined} + ]}, + thrift_test_types:struct_info_ext('thrift.test.VersioningTestV1') + )}, + {"VersioningTestV2 definition", ?_assertEqual( + {struct, [ + {1, undefined, i32, begin_in_both, undefined}, + {2, undefined, i32, newint, undefined}, + {3, undefined, byte, newbyte, undefined}, + {4, undefined, i16, newshort, undefined}, + {5, undefined, i64, newlong, undefined}, + {6, undefined, double, newdouble, undefined}, + {7, undefined, {struct, {thrift_test_types, 'thrift.test.Bonk'}}, newstruct, #'thrift.test.Bonk'{}}, + {8, undefined, {list, i32}, newlist, []}, + {9, undefined, {set, i32}, newset, sets:new()}, + {10, undefined, {map, i32, i32}, newmap, dict:new()}, + {11, undefined, string, newstring, undefined}, + {12, undefined, i32, end_in_both, undefined} + ]}, + thrift_test_types:struct_info_ext('thrift.test.VersioningTestV2') + )}, + {"ListTypeVersioningV1 definition", ?_assertEqual( + {struct, [ + {1, undefined, {list, i32}, myints, []}, + {2, undefined, string, hello, undefined} + ]}, + thrift_test_types:struct_info_ext('thrift.test.ListTypeVersioningV1') + )}, + {"ListTypeVersioningV2 definition", ?_assertEqual( + {struct, [ + {1, undefined, {list, string}, strings, []}, + {2, undefined, string, hello, undefined} + ]}, + thrift_test_types:struct_info_ext('thrift.test.ListTypeVersioningV2') + )}, + {"GuessProtocolStruct definition", ?_assertEqual( + {struct, [ + {7, undefined, {map, string, string}, map_field, dict:new()} + ]}, + thrift_test_types:struct_info_ext('thrift.test.GuessProtocolStruct') + )}, + {"LargeDeltas definition", ?_assertEqual( + {struct, [ + {1, undefined, {struct, {thrift_test_types, 'thrift.test.Bools'}}, b1, #'thrift.test.Bools'{}}, + {10, undefined, {struct, {thrift_test_types, 'thrift.test.Bools'}}, b10, #'thrift.test.Bools'{}}, + {100, undefined, {struct, {thrift_test_types, 'thrift.test.Bools'}}, b100, #'thrift.test.Bools'{}}, + {500, undefined, bool, check_true, undefined}, + {1000, undefined, {struct, {thrift_test_types, 'thrift.test.Bools'}}, b1000, #'thrift.test.Bools'{}}, + {1500, undefined, bool, check_false, undefined}, + {2000, undefined, {struct, {thrift_test_types, 'thrift.test.VersioningTestV2'}}, vertwo2000, #'thrift.test.VersioningTestV2'{}}, + {2500, undefined, {set, string}, a_set2500, sets:new()}, + {3000, undefined, {struct, {thrift_test_types, 'thrift.test.VersioningTestV2'}}, vertwo3000, #'thrift.test.VersioningTestV2'{}}, + {4000, undefined, {list, i32}, big_numbers, []} + ]}, + thrift_test_types:struct_info_ext('thrift.test.LargeDeltas') + )}, + {"NestedListsI32x2 definition", ?_assertEqual( + {struct, [ + {1, undefined, {list, {list, i32}}, integerlist, []} + ]}, + thrift_test_types:struct_info_ext('thrift.test.NestedListsI32x2') + )}, + {"NestedListsI32x3 definition", ?_assertEqual( + {struct, [ + {1, undefined, {list, {list, {list, i32}}}, integerlist, []} + ]}, + thrift_test_types:struct_info_ext('thrift.test.NestedListsI32x3') + )}, + {"NestedMixedx2 definition", ?_assertEqual( + {struct, [ + {1, undefined, {list, {set, i32}}, int_set_list, []}, + {2, undefined, {map, i32, {set, string}}, map_int_strset, dict:new()}, + {3, undefined, {list, {map, i32, {set, string}}}, map_int_strset_list, []} + ]}, + thrift_test_types:struct_info_ext('thrift.test.NestedMixedx2') + )}, + {"ListBonks definition", ?_assertEqual( + {struct, [ + {1, undefined, {list, {struct, {thrift_test_types, 'thrift.test.Bonk'}}}, bonk, []} + ]}, + thrift_test_types:struct_info_ext('thrift.test.ListBonks') + )}, + {"NestedListsBonk definition", ?_assertEqual( + {struct, [ + {1, undefined, {list, {list, {list, {struct, {thrift_test_types, 'thrift.test.Bonk'}}}}}, bonk, []} + ]}, + thrift_test_types:struct_info_ext('thrift.test.NestedListsBonk') + )}, + {"BoolTest definition", ?_assertEqual( + {struct, [ + {1, optional, bool, b, true}, + {2, optional, string, s, "true"} + ]}, + thrift_test_types:struct_info_ext('thrift.test.BoolTest') + )}, + {"StructA definition", ?_assertEqual( + {struct, [{1, required, string, s, undefined}]}, + thrift_test_types:struct_info_ext('thrift.test.StructA') + )}, + {"StructB definition", ?_assertEqual( + {struct, [ + {1, optional, {struct, {thrift_test_types, 'thrift.test.StructA'}}, aa, #'thrift.test.StructA'{}}, + {2, required, {struct, {thrift_test_types, 'thrift.test.StructA'}}, ab, #'thrift.test.StructA'{}} + ]}, + thrift_test_types:struct_info_ext('thrift.test.StructB') + )} + ]. + +service_info_test_() -> + [ + {"testVoid params", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testVoid, params_type) + )}, + {"testVoid reply", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testVoid, reply_type) + )}, + {"testVoid exceptions", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testVoid, exceptions) + )}, + {"testString params", ?_assertEqual( + {struct, [{1, string}]}, + thrift_test_thrift:function_info(testString, params_type) + )}, + {"testString reply", ?_assertEqual( + string, + thrift_test_thrift:function_info(testString, reply_type) + )}, + {"testString exceptions", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testString, exceptions) + )}, + {"testByte params", ?_assertEqual( + {struct, [{1, byte}]}, + thrift_test_thrift:function_info(testByte, params_type) + )}, + {"testByte reply", ?_assertEqual( + byte, + thrift_test_thrift:function_info(testByte, reply_type) + )}, + {"testByte exceptions", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testByte, exceptions) + )}, + {"testI32 params", ?_assertEqual( + {struct, [{1, i32}]}, + thrift_test_thrift:function_info(testI32, params_type) + )}, + {"testI32 reply", ?_assertEqual( + i32, + thrift_test_thrift:function_info(testI32, reply_type) + )}, + {"testI32 exceptions", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testI32, exceptions) + )}, + {"testI64 params", ?_assertEqual( + {struct, [{1, i64}]}, + thrift_test_thrift:function_info(testI64, params_type) + )}, + {"testI64 reply", ?_assertEqual( + i64, + thrift_test_thrift:function_info(testI64, reply_type) + )}, + {"testI64 exceptions", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testI64, exceptions) + )}, + {"testDouble params", ?_assertEqual( + {struct, [{1, double}]}, + thrift_test_thrift:function_info(testDouble, params_type) + )}, + {"testDouble reply", ?_assertEqual( + double, + thrift_test_thrift:function_info(testDouble, reply_type) + )}, + {"testDouble exceptions", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testDouble, exceptions) + )}, + {"testStruct params", ?_assertEqual( + {struct, [ + {1, {struct, {thrift_test_types, 'thrift.test.Xtruct'}}} + ]}, + thrift_test_thrift:function_info(testStruct, params_type) + )}, + {"testStruct reply", ?_assertEqual( + {struct, {thrift_test_types, 'thrift.test.Xtruct'}}, + thrift_test_thrift:function_info(testStruct, reply_type) + )}, + {"testStruct exceptions", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testStruct, exceptions) + )}, + {"testNest params", ?_assertEqual( + {struct, [ + {1, {struct, {thrift_test_types, 'thrift.test.Xtruct2'}}} + ]}, + thrift_test_thrift:function_info(testNest, params_type) + )}, + {"testNest reply", ?_assertEqual( + {struct, {thrift_test_types, 'thrift.test.Xtruct2'}}, + thrift_test_thrift:function_info(testNest, reply_type) + )}, + {"testNest exceptions", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testNest, exceptions) + )}, + {"testMap params", ?_assertEqual( + {struct, [ + {1, {map, i32, i32}} + ]}, + thrift_test_thrift:function_info(testMap, params_type) + )}, + {"testMap reply", ?_assertEqual( + {map, i32, i32}, + thrift_test_thrift:function_info(testMap, reply_type) + )}, + {"testMap exceptions", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testMap, exceptions) + )}, + {"testStringMap params", ?_assertEqual( + {struct, [ + {1, {map, string, string}} + ]}, + thrift_test_thrift:function_info(testStringMap, params_type) + )}, + {"testStringMap reply", ?_assertEqual( + {map, string, string}, + thrift_test_thrift:function_info(testStringMap, reply_type) + )}, + {"testStringMap exceptions", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testStringMap, exceptions) + )}, + {"testSet params", ?_assertEqual( + {struct, [ + {1, {set, i32}} + ]}, + thrift_test_thrift:function_info(testSet, params_type) + )}, + {"testSet reply", ?_assertEqual( + {set, i32}, + thrift_test_thrift:function_info(testSet, reply_type) + )}, + {"testSet exceptions", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testSet, exceptions) + )}, + {"testList params", ?_assertEqual( + {struct, [ + {1, {list, i32}} + ]}, + thrift_test_thrift:function_info(testList, params_type) + )}, + {"testList reply", ?_assertEqual( + {list, i32}, + thrift_test_thrift:function_info(testList, reply_type) + )}, + {"testList exceptions", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testList, exceptions) + )}, + {"testEnum params", ?_assertEqual( + {struct, [ + {1, i32} + ]}, + thrift_test_thrift:function_info(testEnum, params_type) + )}, + {"testEnum reply", ?_assertEqual( + i32, + thrift_test_thrift:function_info(testEnum, reply_type) + )}, + {"testEnum exceptions", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testEnum, exceptions) + )}, + {"testTypedef params", ?_assertEqual( + {struct, [{1, i64}]}, + thrift_test_thrift:function_info(testTypedef, params_type) + )}, + {"testTypedef reply", ?_assertEqual( + i64, + thrift_test_thrift:function_info(testTypedef, reply_type) + )}, + {"testTypedef exceptions", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testTypedef, exceptions) + )}, + {"testMapMap params", ?_assertEqual( + {struct, [ + {1, i32} + ]}, + thrift_test_thrift:function_info(testMapMap, params_type) + )}, + {"testMapMap reply", ?_assertEqual( + {map, i32, {map, i32,i32}}, + thrift_test_thrift:function_info(testMapMap, reply_type) + )}, + {"testMapMap exceptions", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testMapMap, exceptions) + )}, + {"testInsanity params", ?_assertEqual( + {struct, [ + {1, {struct, {thrift_test_types, 'thrift.test.Insanity'}}} + ]}, + thrift_test_thrift:function_info(testInsanity, params_type) + )}, + {"testInsanity reply", ?_assertEqual( + {map, i64, {map, i32, {struct, {'thrift_test_types', 'thrift.test.Insanity'}}}}, + thrift_test_thrift:function_info(testInsanity, reply_type) + )}, + {"testInsanity exceptions", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testInsanity, exceptions) + )}, + {"testMulti params", ?_assertEqual( + {struct, [ + {1, byte}, + {2, i32}, + {3, i64}, + {4, {map, i16, string}}, + {5, i32}, + {6, i64} + ]}, + thrift_test_thrift:function_info(testMulti, params_type) + )}, + {"testMulti reply", ?_assertEqual( + {struct, {thrift_test_types, 'thrift.test.Xtruct'}}, + thrift_test_thrift:function_info(testMulti, reply_type) + )}, + {"testMulti exceptions", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testMulti, exceptions) + )}, + {"testException params", ?_assertEqual( + {struct, [{1, string}]}, + thrift_test_thrift:function_info(testException, params_type) + )}, + {"testException reply", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testException, reply_type) + )}, + {"testException exceptions", ?_assertEqual( + {struct, [ + {1, {struct, {thrift_test_types, 'thrift.test.Xception'}}} + ]}, + thrift_test_thrift:function_info(testException, exceptions) + )}, + {"testMultiException params", ?_assertEqual( + {struct, [{1, string}, {2, string}]}, + thrift_test_thrift:function_info(testMultiException, params_type) + )}, + {"testMultiException reply", ?_assertEqual( + {struct, {thrift_test_types, 'thrift.test.Xtruct'}}, + thrift_test_thrift:function_info(testMultiException, reply_type) + )}, + {"testMultiException exceptions", ?_assertEqual( + {struct, [ + {1, {struct, {thrift_test_types, 'thrift.test.Xception'}}}, + {2, {struct, {thrift_test_types, 'thrift.test.Xception2'}}} + ]}, + thrift_test_thrift:function_info(testMultiException, exceptions) + )}, + {"testOneway params", ?_assertEqual( + {struct, [{1, i32}]}, + thrift_test_thrift:function_info(testOneway, params_type) + )}, + {"testOneway reply", ?_assertEqual( + oneway_void, + thrift_test_thrift:function_info(testOneway, reply_type) + )}, + {"testOneway exceptions", ?_assertEqual( + {struct, []}, + thrift_test_thrift:function_info(testOneway, exceptions) + )}, + {"secondtestString params", ?_assertEqual( + {struct, [{1, string}]}, + second_service_thrift:function_info(secondtestString, params_type) + )}, + {"secondtestString reply", ?_assertEqual( + string, + second_service_thrift:function_info(secondtestString, reply_type) + )}, + {"secondtestString exceptions", ?_assertEqual( + {struct, []}, + second_service_thrift:function_info(secondtestString, exceptions) + )} + ]. -- cgit v1.2.3