THRIFT-4236: Add context support for go server.
Client: Go
Patch: taozle <zhangliyang26@gmail.com>
This closes #1298
diff --git a/Dockerfile b/Dockerfile
index 0d7ad21..2413b91 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -50,7 +50,7 @@
/thrift \
&& cmake --build . --config Release \
&& make install \
- && curl -k -sSL "https://storage.googleapis.com/golang/go1.5.2.linux-amd64.tar.gz" -o /tmp/go.tar.gz \
+ && curl -k -sSL "https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz" -o /tmp/go.tar.gz \
&& tar xzf /tmp/go.tar.gz -C /tmp \
&& cp /tmp/go/bin/gofmt /usr/bin/gofmt \
&& apt-get purge -y --auto-remove $buildDeps \
diff --git a/build/docker/centos/Dockerfile b/build/docker/centos/Dockerfile
index 1881343..974823b 100644
--- a/build/docker/centos/Dockerfile
+++ b/build/docker/centos/Dockerfile
@@ -102,7 +102,7 @@
erlang-tools
# Go Dependencies
-RUN curl -sSL https://storage.googleapis.com/golang/go1.4.3.linux-amd64.tar.gz | tar -C /usr/local/ -xz
+RUN curl -sSL https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz | tar -C /usr/local/ -xz
ENV PATH /usr/local/go/bin:$PATH
# Haskell Dependencies
diff --git a/build/docker/debian/Dockerfile b/build/docker/debian/Dockerfile
index 7bc74fc..8aa0902 100644
--- a/build/docker/debian/Dockerfile
+++ b/build/docker/debian/Dockerfile
@@ -155,7 +155,7 @@
RUN pip3 install -U backports.ssl_match_hostname tornado
# Go
-RUN curl -sSL https://storage.googleapis.com/golang/go1.4.3.linux-amd64.tar.gz | tar -C /usr/local/ -xz
+RUN curl -sSL https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz | tar -C /usr/local/ -xz
ENV PATH /usr/local/go/bin:$PATH
# Haxe
diff --git a/build/docker/ubuntu/Dockerfile b/build/docker/ubuntu/Dockerfile
index a1ff5a1..25089eb 100644
--- a/build/docker/ubuntu/Dockerfile
+++ b/build/docker/ubuntu/Dockerfile
@@ -177,7 +177,7 @@
RUN pip3 install -U backports.ssl_match_hostname tornado
# Go
-RUN curl -sSL https://storage.googleapis.com/golang/go1.4.3.linux-amd64.tar.gz | tar -C /usr/local/ -xz
+RUN curl -sSL https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz | tar -C /usr/local/ -xz
ENV PATH /usr/local/go/bin:$PATH
# Haxe
diff --git a/compiler/cpp/src/thrift/generate/t_go_generator.cc b/compiler/cpp/src/thrift/generate/t_go_generator.cc
index 8c8bda7..499d8c1 100644
--- a/compiler/cpp/src/thrift/generate/t_go_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_go_generator.cc
@@ -81,6 +81,7 @@
gen_package_prefix_ = "";
package_flag = "";
read_write_private_ = false;
+ use_context_ = false;
ignore_initialisms_ = false;
for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
if( iter->first.compare("package_prefix") == 0) {
@@ -91,6 +92,8 @@
package_flag = (iter->second);
} else if( iter->first.compare("read_write_private") == 0) {
read_write_private_ = true;
+ } else if( iter->first.compare("use_context") == 0) {
+ use_context_ = true;
} else if( iter->first.compare("ignore_initialisms") == 0) {
ignore_initialisms_ = true;
} else {
@@ -258,7 +261,8 @@
std::string function_signature(t_function* tfunction, std::string prefix = "");
std::string function_signature_if(t_function* tfunction,
std::string prefix = "",
- bool addError = false);
+ bool addError = false,
+ bool enableContext = false);
std::string argument_list(t_struct* tstruct);
std::string type_to_enum(t_type* ttype);
std::string type_to_go_type(t_type* ttype);
@@ -284,6 +288,7 @@
std::string gen_package_prefix_;
std::string gen_thrift_import_;
bool read_write_private_;
+ bool use_context_;
bool ignore_initialisms_;
/**
@@ -872,12 +877,17 @@
*/
string t_go_generator::go_imports_begin(bool consts) {
string extra;
+
// If not writing constants, and there are enums, need extra imports.
if (!consts && get_program()->get_enums().size() > 0) {
- extra =
+ extra +=
"\t\"database/sql/driver\"\n"
"\t\"errors\"\n";
}
+ if (use_context_) {
+ extra +=
+ "\t\"context\"\n";
+ }
return string(
"import (\n"
"\t\"bytes\"\n"
@@ -894,12 +904,20 @@
* This will have to do in lieu of more intelligent import statement construction
*/
string t_go_generator::go_imports_end() {
+ string extra;
+
+ if (use_context_) {
+ extra +=
+ "var _ = context.Background\n";
+ }
+
return string(
")\n\n"
"// (needed to ensure safety because of naive import list construction.)\n"
"var _ = thrift.ZERO\n"
"var _ = fmt.Printf\n"
"var _ = reflect.DeepEqual\n"
+ + extra +
"var _ = bytes.Equal\n\n");
}
@@ -1824,7 +1842,7 @@
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
generate_go_docstring(f_types_, (*f_iter));
- f_types_ << indent() << function_signature_if(*f_iter, "", true) << endl;
+ f_types_ << indent() << function_signature_if(*f_iter, "", true, use_context_) << endl;
}
}
@@ -1938,7 +1956,7 @@
// Open function
generate_go_docstring(f_types_, (*f_iter));
f_types_ << indent() << "func (p *" << serviceName << "Client) "
- << function_signature_if(*f_iter, "", true) << " {" << endl;
+ << function_signature_if(*f_iter, "", true, false) << " {" << endl;
indent_up();
/*
f_types_ <<
@@ -2586,31 +2604,36 @@
// Generate the header portion
string self(tmp("self"));
+ string processorFunction("thrift.TProcessorFunction");
+ if (use_context_) {
+ processorFunction = "thrift.TProcessorFunction2";
+ }
+
if (extends_processor.empty()) {
f_types_ << indent() << "type " << serviceName << "Processor struct {" << endl;
- f_types_ << indent() << " processorMap map[string]thrift.TProcessorFunction" << endl;
+ f_types_ << indent() << " processorMap map[string]" << processorFunction << endl;
f_types_ << indent() << " handler " << serviceName << endl;
f_types_ << indent() << "}" << endl << endl;
f_types_ << indent() << "func (p *" << serviceName
- << "Processor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) {"
+ << "Processor) AddToProcessorMap(key string, processor " << processorFunction << ") {"
<< endl;
f_types_ << indent() << " p.processorMap[key] = processor" << endl;
f_types_ << indent() << "}" << endl << endl;
f_types_ << indent() << "func (p *" << serviceName
<< "Processor) GetProcessorFunction(key string) "
- "(processor thrift.TProcessorFunction, ok bool) {" << endl;
+ "(processor "<< processorFunction << ", ok bool) {" << endl;
f_types_ << indent() << " processor, ok = p.processorMap[key]" << endl;
f_types_ << indent() << " return processor, ok" << endl;
f_types_ << indent() << "}" << endl << endl;
f_types_ << indent() << "func (p *" << serviceName
- << "Processor) ProcessorMap() map[string]thrift.TProcessorFunction {" << endl;
+ << "Processor) ProcessorMap() map[string]" << processorFunction << "{" << endl;
f_types_ << indent() << " return p.processorMap" << endl;
f_types_ << indent() << "}" << endl << endl;
f_types_ << indent() << "func New" << serviceName << "Processor(handler " << serviceName
<< ") *" << serviceName << "Processor {" << endl << endl;
f_types_
<< indent() << " " << self << " := &" << serviceName
- << "Processor{handler:handler, processorMap:make(map[string]thrift.TProcessorFunction)}"
+ << "Processor{handler:handler, processorMap:make(map[string]" << processorFunction << ")}"
<< endl;
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
@@ -2620,16 +2643,24 @@
<< "{handler:handler}" << endl;
}
+ string ctxParam("");
+ string ctxVar("");
+ if (use_context_) {
+ ctxParam = "ctx context.Context, ";
+ ctxVar = "ctx, ";
+ }
+
string x(tmp("x"));
f_types_ << indent() << "return " << self << endl;
f_types_ << indent() << "}" << endl << endl;
f_types_ << indent() << "func (p *" << serviceName
- << "Processor) Process(iprot, oprot thrift.TProtocol) (success bool, err "
+ << "Processor) Process(" << ctxParam
+ << "iprot, oprot thrift.TProtocol) (success bool, err "
"thrift.TException) {" << endl;
f_types_ << indent() << " name, _, seqId, err := iprot.ReadMessageBegin()" << endl;
f_types_ << indent() << " if err != nil { return false, err }" << endl;
f_types_ << indent() << " if processor, ok := p.GetProcessorFunction(name); ok {" << endl;
- f_types_ << indent() << " return processor.Process(seqId, iprot, oprot)" << endl;
+ f_types_ << indent() << " return processor.Process(" << ctxVar << "seqId, iprot, oprot)" << endl;
f_types_ << indent() << " }" << endl;
f_types_ << indent() << " iprot.Skip(thrift.STRUCT)" << endl;
f_types_ << indent() << " iprot.ReadMessageEnd()" << endl;
@@ -2682,6 +2713,13 @@
+ publicize(tfunction->get_name());
string argsname = publicize(tfunction->get_name() + "_args", true);
string resultname = publicize(tfunction->get_name() + "_result", true);
+
+ string ctxParam("");
+ string ctxVar("");
+ if (use_context_) {
+ ctxParam = "ctx context.Context, ";
+ ctxVar = "ctx";
+ }
// t_struct* xs = tfunction->get_xceptions();
// const std::vector<t_field*>& xceptions = xs->get_members();
vector<t_field*>::const_iterator x_iter;
@@ -2689,7 +2727,7 @@
f_types_ << indent() << " handler " << publicize(tservice->get_name()) << endl;
f_types_ << indent() << "}" << endl << endl;
f_types_ << indent() << "func (p *" << processorName
- << ") Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err "
+ << ") Process(" << ctxParam << "seqId int32, iprot, oprot thrift.TProtocol) (success bool, err "
"thrift.TException) {" << endl;
indent_up();
f_types_ << indent() << "args := " << argsname << "{}" << endl;
@@ -2733,9 +2771,13 @@
f_types_ << "err2 = p.handler." << publicize(tfunction->get_name()) << "(";
bool first = true;
+ f_types_ << ctxVar;
for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
if (first) {
first = false;
+ if (use_context_) {
+ f_types_ << ", ";
+ }
} else {
f_types_ << ", ";
}
@@ -3417,11 +3459,15 @@
* Renders an interface function signature of the form 'type name(args)'
*
* @param tfunction Function definition
+ * @param disableContext Client doesn't suppport context for now.
* @return String of rendered function definition
*/
-string t_go_generator::function_signature_if(t_function* tfunction, string prefix, bool addError) {
+string t_go_generator::function_signature_if(t_function* tfunction, string prefix, bool addError, bool enableContext) {
// TODO(mcslee): Nitpicky, no ',' if argument_list is empty
string signature = publicize(prefix + tfunction->get_name()) + "(";
+ if (enableContext) {
+ signature += "ctx context.Context, ";
+ }
signature += argument_list(tfunction->get_arglist()) + ") (";
t_type* ret = tfunction->get_returntype();
t_struct* exceptions = tfunction->get_xceptions();
@@ -3678,7 +3724,7 @@
// before submitting a patch that enables this feature again. Thank you.
(void) file_path;
return false;
-
+
/*
const string command = "gofmt -w " + file_path;
@@ -3698,4 +3744,6 @@
" ignore_initialisms\n"
" Disable automatic spelling correction of initialisms (e.g. \"URL\")\n" \
" read_write_private\n"
- " Make read/write methods private, default is public Read/Write\n")
+ " Make read/write methods private, default is public Read/Write\n" \
+ " use_context\n"
+ " Make service method receive a context as first argument.\n")
diff --git a/configure.ac b/configure.ac
index 0c628da..fcb9c6d 100755
--- a/configure.ac
+++ b/configure.ac
@@ -397,7 +397,7 @@
AC_PATH_PROG([GO], [go])
if [[ -x "$GO" ]] ; then
AS_IF([test -n "$GO"],[
- ax_go_version="1.4"
+ ax_go_version="1.7"
AC_MSG_CHECKING([for Go version])
golang_version=`$GO version 2>&1 | $SED -e 's/\(go \)\(version \)\(go\)\(@<:@0-9@:>@.@<:@0-9@:>@.@<:@0-9@:>@\)\(@<:@\*@:>@*\).*/\4/'`
diff --git a/doc/install/README.md b/doc/install/README.md
index e37f4ff..9e42115 100644
--- a/doc/install/README.md
+++ b/doc/install/README.md
@@ -39,5 +39,5 @@
* Bit::Vector
* Class::Accessor
* Haxe 3.1.3
-* Go 1.4
+* Go 1.7
* Delphi 2010
diff --git a/lib/go/thrift/http_transport.go b/lib/go/thrift/http_transport.go
index e395d20..d5b5b7c 100644
--- a/lib/go/thrift/http_transport.go
+++ b/lib/go/thrift/http_transport.go
@@ -21,6 +21,7 @@
import (
"compress/gzip"
+ "context"
"io"
"net/http"
"strings"
@@ -38,6 +39,18 @@
})
}
+// NewThriftHandlerFunc2 is same as NewThriftHandlerFunc but requires a Context as its first param.
+func NewThriftHandlerFunc2(ctx context.Context, processor TProcessor2,
+ inPfactory, outPfactory TProtocolFactory) func(w http.ResponseWriter, r *http.Request) {
+
+ return gz(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Add("Content-Type", "application/x-thrift")
+
+ transport := NewStreamTransport(r.Body, w)
+ processor.Process(ctx, inPfactory.GetProtocol(transport), outPfactory.GetProtocol(transport))
+ })
+}
+
// gz transparently compresses the HTTP response if the client supports it.
func gz(handler http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
diff --git a/lib/go/thrift/multiplexed_processor.go b/lib/go/thrift/multiplexed_processor.go
new file mode 100644
index 0000000..d205db8
--- /dev/null
+++ b/lib/go/thrift/multiplexed_processor.go
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "context"
+ "fmt"
+ "strings"
+)
+
+/*
+TMultiplexedProcessor2 is a TProcessor allowing
+a single TServer to provide multiple services with
+context support in TProcessor.
+
+To do so, you instantiate the processor and then register additional
+processors with it, as shown in the following example:
+
+var processor = thrift.NewTMultiplexedProcessor2()
+
+firstProcessor :=
+processor.RegisterProcessor("FirstService", firstProcessor)
+
+processor.registerProcessor(
+ "Calculator",
+ Calculator.NewCalculatorProcessor(&CalculatorHandler{}),
+)
+
+processor.registerProcessor(
+ "WeatherReport",
+ WeatherReport.NewWeatherReportProcessor(&WeatherReportHandler{}),
+)
+
+serverTransport, err := thrift.NewTServerSocketTimeout(addr, TIMEOUT)
+if err != nil {
+ t.Fatal("Unable to create server socket", err)
+}
+server := thrift.NewTSimpleServer2(processor, serverTransport)
+server.Serve();
+*/
+
+type TMultiplexedProcessor2 struct {
+ serviceProcessorMap map[string]TProcessor2
+ DefaultProcessor TProcessor2
+}
+
+func NewTMultiplexedProcessor2() *TMultiplexedProcessor2 {
+ return &TMultiplexedProcessor2{
+ serviceProcessorMap: make(map[string]TProcessor2),
+ }
+}
+
+func (t *TMultiplexedProcessor2) RegisterDefault(processor TProcessor2) {
+ t.DefaultProcessor = processor
+}
+
+func (t *TMultiplexedProcessor2) RegisterProcessor(name string, processor TProcessor2) {
+ if t.serviceProcessorMap == nil {
+ t.serviceProcessorMap = make(map[string]TProcessor2)
+ }
+ t.serviceProcessorMap[name] = processor
+}
+
+func (t *TMultiplexedProcessor2) Process(ctx context.Context, in, out TProtocol) (bool, TException) {
+ name, typeId, seqid, err := in.ReadMessageBegin()
+ if err != nil {
+ return false, err
+ }
+ if typeId != CALL && typeId != ONEWAY {
+ return false, fmt.Errorf("Unexpected message type %v", typeId)
+ }
+ //extract the service name
+ v := strings.SplitN(name, MULTIPLEXED_SEPARATOR, 2)
+ if len(v) != 2 {
+ if t.DefaultProcessor != nil {
+ smb := NewStoredMessageProtocol(in, name, typeId, seqid)
+ return t.DefaultProcessor.Process(ctx, smb, out)
+ }
+ return false, fmt.Errorf("Service name not found in message name: %s. Did you forget to use a TMultiplexProtocol in your client?", name)
+ }
+ actualProcessor, ok := t.serviceProcessorMap[v[0]]
+ if !ok {
+ return false, fmt.Errorf("Service name not found: %s. Did you forget to call registerProcessor()?", v[0])
+ }
+ smb := NewStoredMessageProtocol(in, v[1], typeId, seqid)
+ return actualProcessor.Process(ctx, smb, out)
+}
diff --git a/lib/go/thrift/multiplexed_protocol.go b/lib/go/thrift/multiplexed_protocol.go
index 3157e0d..6ae7be6 100644
--- a/lib/go/thrift/multiplexed_protocol.go
+++ b/lib/go/thrift/multiplexed_protocol.go
@@ -76,33 +76,11 @@
}
/*
-TMultiplexedProcessor is a TProcessor allowing
-a single TServer to provide multiple services.
+This is the non-context version for TProcessor.
-To do so, you instantiate the processor and then register additional
-processors with it, as shown in the following example:
+See description at file: multiplexed_processor.go
-var processor = thrift.NewTMultiplexedProcessor()
-
-firstProcessor :=
-processor.RegisterProcessor("FirstService", firstProcessor)
-
-processor.registerProcessor(
- "Calculator",
- Calculator.NewCalculatorProcessor(&CalculatorHandler{}),
-)
-
-processor.registerProcessor(
- "WeatherReport",
- WeatherReport.NewWeatherReportProcessor(&WeatherReportHandler{}),
-)
-
-serverTransport, err := thrift.NewTServerSocketTimeout(addr, TIMEOUT)
-if err != nil {
- t.Fatal("Unable to create server socket", err)
-}
-server := thrift.NewTSimpleServer2(processor, serverTransport)
-server.Serve();
+Deprecated: use TMultiplexedProcessor2 for strong server programming.
*/
type TMultiplexedProcessor struct {
diff --git a/lib/go/thrift/processor.go b/lib/go/thrift/processor.go
index ca0d3fa..7f8f365 100644
--- a/lib/go/thrift/processor.go
+++ b/lib/go/thrift/processor.go
@@ -19,6 +19,8 @@
package thrift
+import "context"
+
// A processor is a generic object which operates upon an input stream and
// writes to some output stream.
type TProcessor interface {
@@ -28,3 +30,12 @@
type TProcessorFunction interface {
Process(seqId int32, in, out TProtocol) (bool, TException)
}
+
+// TProcessor2 is TProcessor with ctx as its first argument.
+type TProcessor2 interface {
+ Process(ctx context.Context, in, out TProtocol) (bool, TException)
+}
+
+type TProcessorFunction2 interface {
+ Process(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException)
+}
diff --git a/lib/go/thrift/processor_factory2.go b/lib/go/thrift/processor_factory2.go
new file mode 100644
index 0000000..bd587ad
--- /dev/null
+++ b/lib/go/thrift/processor_factory2.go
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+// The default processor2 factory just returns a singleton
+// instance.
+// The TProcessorFactory2 is a context version of the orignal.
+type TProcessorFactory2 interface {
+ GetProcessor(trans TTransport) TProcessor2
+}
+
+type tProcessorFactory2 struct {
+ processor TProcessor2
+}
+
+func NewTProcessorFactory2(p TProcessor2) TProcessorFactory2 {
+ return &tProcessorFactory2{processor: p}
+}
+
+func (p *tProcessorFactory2) GetProcessor(trans TTransport) TProcessor2 {
+ return p.processor
+}
+
+/**
+ * The default processor factory2 just returns a singleton
+ * instance.
+ */
+type TProcessorFunctionFactory2 interface {
+ GetProcessorFunction(trans TTransport) TProcessorFunction2
+}
+
+type tProcessorFunctionFactory2 struct {
+ processor TProcessorFunction2
+}
+
+func NewTProcessorFunctionFactory2(p TProcessorFunction2) TProcessorFunctionFactory2 {
+ return &tProcessorFunctionFactory2{processor: p}
+}
+
+func (p *tProcessorFunctionFactory2) GetProcessorFunction(trans TTransport) TProcessorFunction2 {
+ return p.processor
+}
diff --git a/lib/go/thrift/server.go b/lib/go/thrift/server.go
index f813fa3..3e8a656 100644
--- a/lib/go/thrift/server.go
+++ b/lib/go/thrift/server.go
@@ -33,3 +33,19 @@
// all servers are required to be cleanly stoppable.
Stop() error
}
+
+// Same as TServer but use TProcessorFactory2 as processor factory.
+type TServer2 interface {
+ ProcessorFactory() TProcessorFactory2
+ ServerTransport() TServerTransport
+ InputTransportFactory() TTransportFactory
+ OutputTransportFactory() TTransportFactory
+ InputProtocolFactory() TProtocolFactory
+ OutputProtocolFactory() TProtocolFactory
+
+ // Starts the server
+ Serve() error
+ // Stops the server. This is optional on a per-implementation basis. Not
+ // all servers are required to be cleanly stoppable.
+ Stop() error
+}
diff --git a/lib/go/thrift/simple_server2.go b/lib/go/thrift/simple_server2.go
new file mode 100644
index 0000000..9e9961f
--- /dev/null
+++ b/lib/go/thrift/simple_server2.go
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package thrift
+
+import (
+ "context"
+ "log"
+ "runtime/debug"
+ "sync"
+)
+
+/*
+ * This is only a simple sample same as TSimpleServer but add context
+ * usage support.
+ */
+type TSimpleServer2 struct {
+ quit chan struct{}
+
+ processorFactory TProcessorFactory2
+ serverTransport TServerTransport
+ inputTransportFactory TTransportFactory
+ outputTransportFactory TTransportFactory
+ inputProtocolFactory TProtocolFactory
+ outputProtocolFactory TProtocolFactory
+ sync.WaitGroup
+}
+
+func NewTSimpleServerWithContext(processor TProcessor2, serverTransport TServerTransport) *TSimpleServer2 {
+ return NewTSimpleServerFactoryWithContext(NewTProcessorFactory2(processor), serverTransport)
+}
+
+func NewTSimpleServerFactoryWithContext(processorFactory TProcessorFactory2, serverTransport TServerTransport) *TSimpleServer2 {
+ return &TSimpleServer2{
+ quit: make(chan struct{}, 1),
+ processorFactory: processorFactory,
+ serverTransport: serverTransport,
+ inputTransportFactory: NewTTransportFactory(),
+ outputTransportFactory: NewTTransportFactory(),
+ inputProtocolFactory: NewTBinaryProtocolFactoryDefault(),
+ outputProtocolFactory: NewTBinaryProtocolFactoryDefault(),
+ }
+}
+
+func (p *TSimpleServer2) ProcessorFactory() TProcessorFactory2 {
+ return p.processorFactory
+}
+
+func (p *TSimpleServer2) ServerTransport() TServerTransport {
+ return p.serverTransport
+}
+
+func (p *TSimpleServer2) InputTransportFactory() TTransportFactory {
+ return p.inputTransportFactory
+}
+
+func (p *TSimpleServer2) OutputTransportFactory() TTransportFactory {
+ return p.outputTransportFactory
+}
+
+func (p *TSimpleServer2) InputProtocolFactory() TProtocolFactory {
+ return p.inputProtocolFactory
+}
+
+func (p *TSimpleServer2) OutputProtocolFactory() TProtocolFactory {
+ return p.outputProtocolFactory
+}
+
+func (p *TSimpleServer2) Listen() error {
+ return p.serverTransport.Listen()
+}
+
+func (p *TSimpleServer2) AcceptLoop() error {
+ for {
+ client, err := p.serverTransport.Accept()
+ if err != nil {
+ select {
+ case <-p.quit:
+ return nil
+ default:
+ }
+ return err
+ }
+ if client != nil {
+ p.Add(1)
+ go func() {
+ if err := p.processRequests(client); err != nil {
+ log.Println("error processing request:", err)
+ }
+ }()
+ }
+ }
+}
+
+func (p *TSimpleServer2) Serve() error {
+ err := p.Listen()
+ if err != nil {
+ return err
+ }
+ p.AcceptLoop()
+ return nil
+}
+
+var once2 sync.Once
+
+func (p *TSimpleServer2) Stop() error {
+ q := func() {
+ close(p.quit)
+ p.serverTransport.Interrupt()
+ p.Wait()
+ }
+ once2.Do(q)
+ return nil
+}
+
+func (p *TSimpleServer2) processRequests(client TTransport) error {
+ defer p.Done()
+
+ processor := p.processorFactory.GetProcessor(client)
+ inputTransport, err := p.inputTransportFactory.GetTransport(client)
+ if err != nil {
+ return err
+ }
+ outputTransport, err := p.outputTransportFactory.GetTransport(client)
+ if err != nil {
+ return err
+ }
+ inputProtocol := p.inputProtocolFactory.GetProtocol(inputTransport)
+ outputProtocol := p.outputProtocolFactory.GetProtocol(outputTransport)
+ defer func() {
+ if e := recover(); e != nil {
+ log.Printf("panic in processor: %s: %s", e, debug.Stack())
+ }
+ }()
+
+ if inputTransport != nil {
+ defer inputTransport.Close()
+ }
+ if outputTransport != nil {
+ defer outputTransport.Close()
+ }
+ for {
+ select {
+ case <-p.quit:
+ return nil
+ default:
+ }
+
+ ctx := context.Background()
+ ok, err := processor.Process(ctx, inputProtocol, outputProtocol)
+ if err, ok := err.(TTransportException); ok && err.TypeId() == END_OF_FILE {
+ return nil
+ } else if err != nil {
+ return err
+ }
+ if err, ok := err.(TApplicationException); ok && err.TypeId() == UNKNOWN_METHOD {
+ continue
+ }
+ if !ok {
+ break
+ }
+ }
+ return nil
+}