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