1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 module thrift.server.simple;
20 
21 import std.variant : Variant;
22 import thrift.base;
23 import thrift.protocol.base;
24 import thrift.protocol.processor;
25 import thrift.server.base;
26 import thrift.server.transport.base;
27 import thrift.transport.base;
28 import thrift.util.cancellation;
29 
30 /**
31  * The most basic server.
32  *
33  * It is single-threaded and after it accepts a connections, it processes
34  * requests on it until it closes, then waiting for the next connection.
35  *
36  * It is not so much of use in production than it is for writing unittests, or
37  * as an example on how to provide a custom TServer implementation.
38  */
39 class TSimpleServer : TServer {
40   ///
41   this(
42     TProcessor processor,
43     TServerTransport serverTransport,
44     TTransportFactory transportFactory,
45     TProtocolFactory protocolFactory
46   ) {
47     super(processor, serverTransport, transportFactory, protocolFactory);
48   }
49 
50   ///
51   this(
52     TProcessorFactory processorFactory,
53     TServerTransport serverTransport,
54     TTransportFactory transportFactory,
55     TProtocolFactory protocolFactory
56   ) {
57     super(processorFactory, serverTransport, transportFactory, protocolFactory);
58   }
59 
60   ///
61   this(
62     TProcessor processor,
63     TServerTransport serverTransport,
64     TTransportFactory inputTransportFactory,
65     TTransportFactory outputTransportFactory,
66     TProtocolFactory inputProtocolFactory,
67     TProtocolFactory outputProtocolFactory
68   ) {
69     super(processor, serverTransport, inputTransportFactory,
70       outputTransportFactory, inputProtocolFactory, outputProtocolFactory);
71   }
72 
73   this(
74     TProcessorFactory processorFactory,
75     TServerTransport serverTransport,
76     TTransportFactory inputTransportFactory,
77     TTransportFactory outputTransportFactory,
78     TProtocolFactory inputProtocolFactory,
79     TProtocolFactory outputProtocolFactory
80   ) {
81     super(processorFactory, serverTransport, inputTransportFactory,
82       outputTransportFactory, inputProtocolFactory, outputProtocolFactory);
83   }
84 
85   override void serve(TCancellation cancellation = null) {
86     serverTransport_.listen();
87 
88     if (eventHandler) eventHandler.preServe();
89 
90     while (true) {
91       TTransport client;
92       TTransport inputTransport;
93       TTransport outputTransport;
94       TProtocol inputProtocol;
95       TProtocol outputProtocol;
96 
97       try {
98         client = serverTransport_.accept(cancellation);
99         scope(failure) client.close();
100 
101         inputTransport = inputTransportFactory_.getTransport(client);
102         scope(failure) inputTransport.close();
103 
104         outputTransport = outputTransportFactory_.getTransport(client);
105         scope(failure) outputTransport.close();
106 
107         inputProtocol = inputProtocolFactory_.getProtocol(inputTransport);
108         outputProtocol = outputProtocolFactory_.getProtocol(outputTransport);
109       } catch (TCancelledException tcx) {
110         break;
111       } catch (TTransportException ttx) {
112         logError("TServerTransport failed on accept: %s", ttx);
113         continue;
114       } catch (TException tx) {
115         logError("Caught TException on accept: %s", tx);
116         continue;
117       }
118 
119       auto info = TConnectionInfo(inputProtocol, outputProtocol, client);
120       auto processor = processorFactory_.getProcessor(info);
121 
122       Variant connectionContext;
123       if (eventHandler) {
124         connectionContext =
125           eventHandler.createContext(inputProtocol, outputProtocol);
126       }
127 
128       try {
129         while (true) {
130           if (eventHandler) {
131             eventHandler.preProcess(connectionContext, client);
132           }
133 
134           if (!processor.process(inputProtocol, outputProtocol,
135             connectionContext) || !inputProtocol.transport.peek()
136           ) {
137             // Something went fundamentlly wrong or there is nothing more to
138             // process, close the connection.
139             break;
140           }
141         }
142       } catch (TTransportException ttx) {
143         if (ttx.type() != TTransportException.Type.END_OF_FILE) {
144           logError("Client died unexpectedly: %s", ttx);
145         }
146       } catch (Exception e) {
147         logError("Uncaught exception: %s", e);
148       }
149 
150       if (eventHandler) {
151         eventHandler.deleteContext(connectionContext, inputProtocol,
152           outputProtocol);
153       }
154 
155       try {
156         inputTransport.close();
157       } catch (TTransportException ttx) {
158         logError("Input close failed: %s", ttx);
159       }
160       try {
161         outputTransport.close();
162       } catch (TTransportException ttx) {
163         logError("Output close failed: %s", ttx);
164       }
165       try {
166         client.close();
167       } catch (TTransportException ttx) {
168         logError("Client close failed: %s", ttx);
169       }
170     }
171 
172     try {
173       serverTransport_.close();
174     } catch (TServerTransportException e) {
175       logError("Server transport failed to close(): %s", e);
176     }
177   }
178 }
179 
180 unittest {
181   import thrift.internal.test.server;
182   testServeCancel!TSimpleServer();
183 }