]> git.proxmox.com Git - ceph.git/blob - ceph/src/jaegertracing/thrift/lib/d/src/thrift/codegen/processor.d
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / jaegertracing / thrift / lib / d / src / thrift / codegen / processor.d
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.codegen.processor;
20
21 import std.algorithm : find;
22 import std.array : empty, front;
23 import std.conv : to;
24 import std.traits : ParameterTypeTuple, ReturnType, Unqual;
25 import std.typetuple : allSatisfy, TypeTuple;
26 import std.variant : Variant;
27 import thrift.base;
28 import thrift.codegen.base;
29 import thrift.internal.codegen;
30 import thrift.internal.ctfe;
31 import thrift.protocol.base;
32 import thrift.protocol.processor;
33
34 /**
35 * Service processor for Interface, which implements TProcessor by
36 * synchronously forwarding requests for the service methods to a handler
37 * implementing Interface.
38 *
39 * The generated class implements TProcessor and additionally allows a
40 * TProcessorEventHandler to be specified via the public eventHandler property.
41 * The constructor takes a single argument of type Interface, which is the
42 * handler to forward the requests to:
43 * ---
44 * this(Interface iface);
45 * TProcessorEventHandler eventHandler;
46 * ---
47 *
48 * If Interface is derived from another service BaseInterface, this class is
49 * also derived from TServiceProcessor!BaseInterface.
50 *
51 * The optional Protocols template tuple parameter can be used to specify
52 * one or more TProtocol implementations to specifically generate code for. If
53 * the actual types of the protocols passed to process() at runtime match one
54 * of the items from the list, the optimized code paths are taken, otherwise,
55 * a generic TProtocol version is used as fallback. For cases where the input
56 * and output protocols differ, TProtocolPair!(InputProtocol, OutputProtocol)
57 * can be used in the Protocols list:
58 * ---
59 * interface FooService { void foo(); }
60 * class FooImpl { override void foo {} }
61 *
62 * // Provides fast path if TBinaryProtocol!TBufferedTransport is used for
63 * // both input and output:
64 * alias TServiceProcessor!(FooService, TBinaryProtocol!TBufferedTransport)
65 * BinaryProcessor;
66 *
67 * auto proc = new BinaryProcessor(new FooImpl());
68 *
69 * // Low overhead.
70 * proc.process(tBinaryProtocol(tBufferTransport(someSocket)));
71 *
72 * // Not in the specialization list – higher overhead.
73 * proc.process(tBinaryProtocol(tFramedTransport(someSocket)));
74 *
75 * // Same as above, but optimized for the Compact protocol backed by a
76 * // TPipedTransport for input and a TBufferedTransport for output.
77 * alias TServiceProcessor!(FooService, TProtocolPair!(
78 * TCompactProtocol!TPipedTransport, TCompactProtocol!TBufferedTransport)
79 * ) MixedProcessor;
80 * ---
81 */
82 template TServiceProcessor(Interface, Protocols...) if (
83 isService!Interface && allSatisfy!(isTProtocolOrPair, Protocols)
84 ) {
85 mixin({
86 static if (is(Interface BaseInterfaces == super) && BaseInterfaces.length > 0) {
87 static assert(BaseInterfaces.length == 1,
88 "Services cannot be derived from more than one parent.");
89
90 string code = "class TServiceProcessor : " ~
91 "TServiceProcessor!(BaseService!Interface) {\n";
92 code ~= "private Interface iface_;\n";
93
94 string constructorCode = "this(Interface iface) {\n";
95 constructorCode ~= "super(iface);\n";
96 constructorCode ~= "iface_ = iface;\n";
97 } else {
98 string code = "class TServiceProcessor : TProcessor {";
99 code ~= q{
100 override bool process(TProtocol iprot, TProtocol oprot,
101 Variant context = Variant()
102 ) {
103 auto msg = iprot.readMessageBegin();
104
105 void writeException(TApplicationException e) {
106 oprot.writeMessageBegin(TMessage(msg.name, TMessageType.EXCEPTION,
107 msg.seqid));
108 e.write(oprot);
109 oprot.writeMessageEnd();
110 oprot.transport.writeEnd();
111 oprot.transport.flush();
112 }
113
114 if (msg.type != TMessageType.CALL && msg.type != TMessageType.ONEWAY) {
115 skip(iprot, TType.STRUCT);
116 iprot.readMessageEnd();
117 iprot.transport.readEnd();
118
119 writeException(new TApplicationException(
120 TApplicationException.Type.INVALID_MESSAGE_TYPE));
121 return false;
122 }
123
124 auto dg = msg.name in processMap_;
125 if (!dg) {
126 skip(iprot, TType.STRUCT);
127 iprot.readMessageEnd();
128 iprot.transport.readEnd();
129
130 writeException(new TApplicationException("Invalid method name: '" ~
131 msg.name ~ "'.", TApplicationException.Type.INVALID_MESSAGE_TYPE));
132
133 return false;
134 }
135
136 (*dg)(msg.seqid, iprot, oprot, context);
137 return true;
138 }
139
140 TProcessorEventHandler eventHandler;
141
142 alias void delegate(int, TProtocol, TProtocol, Variant) ProcessFunc;
143 protected ProcessFunc[string] processMap_;
144 private Interface iface_;
145 };
146
147 string constructorCode = "this(Interface iface) {\n";
148 constructorCode ~= "iface_ = iface;\n";
149 }
150
151 // Generate the handling code for each method, consisting of the dispatch
152 // function, registering it in the constructor, and the actual templated
153 // handler function.
154 foreach (methodName;
155 FilterMethodNames!(Interface, __traits(derivedMembers, Interface))
156 ) {
157 // Register the processing function in the constructor.
158 immutable procFuncName = "process_" ~ methodName;
159 immutable dispatchFuncName = procFuncName ~ "_protocolDispatch";
160 constructorCode ~= "processMap_[`" ~ methodName ~ "`] = &" ~
161 dispatchFuncName ~ ";\n";
162
163 bool methodMetaFound;
164 TMethodMeta methodMeta;
165 static if (is(typeof(Interface.methodMeta) : TMethodMeta[])) {
166 enum meta = find!`a.name == b`(Interface.methodMeta, methodName);
167 if (!meta.empty) {
168 methodMetaFound = true;
169 methodMeta = meta.front;
170 }
171 }
172
173 // The dispatch function to call the specialized handler functions. We
174 // test the protocols if they can be converted to one of the passed
175 // protocol types, and if not, fall back to the generic TProtocol
176 // version of the processing function.
177 code ~= "void " ~ dispatchFuncName ~
178 "(int seqid, TProtocol iprot, TProtocol oprot, Variant context) {\n";
179 code ~= "foreach (Protocol; TypeTuple!(Protocols, TProtocol)) {\n";
180 code ~= q{
181 static if (is(Protocol _ : TProtocolPair!(I, O), I, O)) {
182 alias I IProt;
183 alias O OProt;
184 } else {
185 alias Protocol IProt;
186 alias Protocol OProt;
187 }
188 auto castedIProt = cast(IProt)iprot;
189 auto castedOProt = cast(OProt)oprot;
190 };
191 code ~= "if (castedIProt && castedOProt) {\n";
192 code ~= procFuncName ~
193 "!(IProt, OProt)(seqid, castedIProt, castedOProt, context);\n";
194 code ~= "return;\n";
195 code ~= "}\n";
196 code ~= "}\n";
197 code ~= "throw new TException(`Internal error: Null iprot/oprot " ~
198 "passed to processor protocol dispatch function.`);\n";
199 code ~= "}\n";
200
201 // The actual handler function, templated on the input and output
202 // protocol types.
203 code ~= "void " ~ procFuncName ~ "(IProt, OProt)(int seqid, IProt " ~
204 "iprot, OProt oprot, Variant connectionContext) " ~
205 "if (isTProtocol!IProt && isTProtocol!OProt) {\n";
206 code ~= "TArgsStruct!(Interface, `" ~ methodName ~ "`) args;\n";
207
208 // Store the (qualified) method name in a manifest constant to avoid
209 // having to litter the code below with lots of string manipulation.
210 code ~= "enum methodName = `" ~ methodName ~ "`;\n";
211
212 code ~= q{
213 enum qName = Interface.stringof ~ "." ~ methodName;
214
215 Variant callContext;
216 if (eventHandler) {
217 callContext = eventHandler.createContext(qName, connectionContext);
218 }
219
220 scope (exit) {
221 if (eventHandler) {
222 eventHandler.deleteContext(callContext, qName);
223 }
224 }
225
226 if (eventHandler) eventHandler.preRead(callContext, qName);
227
228 args.read(iprot);
229 iprot.readMessageEnd();
230 iprot.transport.readEnd();
231
232 if (eventHandler) eventHandler.postRead(callContext, qName);
233 };
234
235 code ~= "TResultStruct!(Interface, `" ~ methodName ~ "`) result;\n";
236 code ~= "try {\n";
237
238 // Generate the parameter list to pass to the called iface function.
239 string[] paramList;
240 foreach (i, _; ParameterTypeTuple!(mixin("Interface." ~ methodName))) {
241 string paramName;
242 if (methodMetaFound && i < methodMeta.params.length) {
243 paramName = methodMeta.params[i].name;
244 } else {
245 paramName = "param" ~ to!string(i + 1);
246 }
247 paramList ~= "args." ~ paramName;
248 }
249
250 immutable call = "iface_." ~ methodName ~ "(" ~ ctfeJoin(paramList) ~ ")";
251 if (is(ReturnType!(mixin("Interface." ~ methodName)) == void)) {
252 code ~= call ~ ";\n";
253 } else {
254 code ~= "result.set!`success`(" ~ call ~ ");\n";
255 }
256
257 // If this is not a oneway method, generate the receiving code.
258 if (!methodMetaFound || methodMeta.type != TMethodType.ONEWAY) {
259 if (methodMetaFound) {
260 foreach (e; methodMeta.exceptions) {
261 code ~= "} catch (Interface." ~ e.type ~ " " ~ e.name ~ ") {\n";
262 code ~= "result.set!`" ~ e.name ~ "`(" ~ e.name ~ ");\n";
263 }
264 }
265 code ~= "}\n";
266
267 code ~= q{
268 catch (Exception e) {
269 if (eventHandler) {
270 eventHandler.handlerError(callContext, qName, e);
271 }
272
273 auto x = new TApplicationException(to!string(e));
274 oprot.writeMessageBegin(
275 TMessage(methodName, TMessageType.EXCEPTION, seqid));
276 x.write(oprot);
277 oprot.writeMessageEnd();
278 oprot.transport.writeEnd();
279 oprot.transport.flush();
280 return;
281 }
282
283 if (eventHandler) eventHandler.preWrite(callContext, qName);
284
285 oprot.writeMessageBegin(TMessage(methodName,
286 TMessageType.REPLY, seqid));
287 result.write(oprot);
288 oprot.writeMessageEnd();
289 oprot.transport.writeEnd();
290 oprot.transport.flush();
291
292 if (eventHandler) eventHandler.postWrite(callContext, qName);
293 };
294 } else {
295 // For oneway methods, we obviously cannot notify the client of any
296 // exceptions, just call the event handler if one is set.
297 code ~= "}\n";
298 code ~= q{
299 catch (Exception e) {
300 if (eventHandler) {
301 eventHandler.handlerError(callContext, qName, e);
302 }
303 return;
304 }
305
306 if (eventHandler) eventHandler.onewayComplete(callContext, qName);
307 };
308 }
309 code ~= "}\n";
310
311 }
312
313 code ~= constructorCode ~ "}\n";
314 code ~= "}\n";
315
316 return code;
317 }());
318 }
319
320 /**
321 * A struct representing the arguments of a Thrift method call.
322 *
323 * There should usually be no reason to use this directly without the help of
324 * TServiceProcessor, but it is documented publicly to help debugging in case
325 * of CTFE errors.
326 *
327 * Consider this example:
328 * ---
329 * interface Foo {
330 * int bar(string a, bool b);
331 *
332 * enum methodMeta = [
333 * TMethodMeta("bar", [TParamMeta("a", 1), TParamMeta("b", 2)])
334 * ];
335 * }
336 *
337 * alias TArgsStruct!(Foo, "bar") FooBarArgs;
338 * ---
339 *
340 * The definition of FooBarArgs is equivalent to:
341 * ---
342 * struct FooBarArgs {
343 * string a;
344 * bool b;
345 *
346 * mixin TStructHelpers!([TFieldMeta("a", 1, TReq.OPT_IN_REQ_OUT),
347 * TFieldMeta("b", 2, TReq.OPT_IN_REQ_OUT)]);
348 * }
349 * ---
350 *
351 * If the TVerboseCodegen version is defined, a warning message is issued at
352 * compilation if no TMethodMeta for Interface.methodName is found.
353 */
354 template TArgsStruct(Interface, string methodName) {
355 static assert(is(typeof(mixin("Interface." ~ methodName))),
356 "Could not find method '" ~ methodName ~ "' in '" ~ Interface.stringof ~ "'.");
357 mixin({
358 bool methodMetaFound;
359 TMethodMeta methodMeta;
360 static if (is(typeof(Interface.methodMeta) : TMethodMeta[])) {
361 auto meta = find!`a.name == b`(Interface.methodMeta, methodName);
362 if (!meta.empty) {
363 methodMetaFound = true;
364 methodMeta = meta.front;
365 }
366 }
367
368 string memberCode;
369 string[] fieldMetaCodes;
370 foreach (i, _; ParameterTypeTuple!(mixin("Interface." ~ methodName))) {
371 // If we have no meta information, just use param1, param2, etc. as
372 // field names, it shouldn't really matter anyway. 1-based »indexing«
373 // is used to match the common scheme in the Thrift world.
374 string memberId;
375 string memberName;
376 if (methodMetaFound && i < methodMeta.params.length) {
377 memberId = to!string(methodMeta.params[i].id);
378 memberName = methodMeta.params[i].name;
379 } else {
380 memberId = to!string(i + 1);
381 memberName = "param" ~ to!string(i + 1);
382 }
383
384 // Unqual!() is needed to generate mutable fields for ref const()
385 // struct parameters.
386 memberCode ~= "Unqual!(ParameterTypeTuple!(Interface." ~ methodName ~
387 ")[" ~ to!string(i) ~ "])" ~ memberName ~ ";\n";
388
389 fieldMetaCodes ~= "TFieldMeta(`" ~ memberName ~ "`, " ~ memberId ~
390 ", TReq.OPT_IN_REQ_OUT)";
391 }
392
393 string code = "struct TArgsStruct {\n";
394 code ~= memberCode;
395 version (TVerboseCodegen) {
396 if (!methodMetaFound &&
397 ParameterTypeTuple!(mixin("Interface." ~ methodName)).length > 0)
398 {
399 code ~= "pragma(msg, `[thrift.codegen.processor.TArgsStruct] Warning: No " ~
400 "meta information for method '" ~ methodName ~ "' in service '" ~
401 Interface.stringof ~ "' found.`);\n";
402 }
403 }
404 immutable fieldMetaCode =
405 fieldMetaCodes.empty ? "" : "[" ~ ctfeJoin(fieldMetaCodes) ~ "]";
406 code ~= "mixin TStructHelpers!(" ~ fieldMetaCode ~ ");\n";
407 code ~= "}\n";
408 return code;
409 }());
410 }
411
412 /**
413 * A struct representing the result of a Thrift method call.
414 *
415 * It contains a field called "success" for the return value of the function
416 * (with id 0), and additional fields for the exceptions declared for the
417 * method, if any.
418 *
419 * There should usually be no reason to use this directly without the help of
420 * TServiceProcessor, but it is documented publicly to help debugging in case
421 * of CTFE errors.
422 *
423 * Consider the following example:
424 * ---
425 * interface Foo {
426 * int bar(string a);
427 *
428 * alias .FooException FooException;
429 *
430 * enum methodMeta = [
431 * TMethodMeta("bar",
432 * [TParamMeta("a", 1)],
433 * [TExceptionMeta("fooe", 1, "FooException")]
434 * )
435 * ];
436 * }
437 * alias TResultStruct!(Foo, "bar") FooBarResult;
438 * ---
439 *
440 * The definition of FooBarResult is equivalent to:
441 * ---
442 * struct FooBarResult {
443 * int success;
444 * FooException fooe;
445 *
446 * mixin(TStructHelpers!([TFieldMeta("success", 0, TReq.OPTIONAL),
447 * TFieldMeta("fooe", 1, TReq.OPTIONAL)]));
448 * }
449 * ---
450 *
451 * If the TVerboseCodegen version is defined, a warning message is issued at
452 * compilation if no TMethodMeta for Interface.methodName is found.
453 */
454 template TResultStruct(Interface, string methodName) {
455 static assert(is(typeof(mixin("Interface." ~ methodName))),
456 "Could not find method '" ~ methodName ~ "' in '" ~ Interface.stringof ~ "'.");
457
458 mixin({
459 string code = "struct TResultStruct {\n";
460
461 string[] fieldMetaCodes;
462
463 static if (!is(ReturnType!(mixin("Interface." ~ methodName)) == void)) {
464 code ~= "ReturnType!(Interface." ~ methodName ~ ") success;\n";
465 fieldMetaCodes ~= "TFieldMeta(`success`, 0, TReq.OPTIONAL)";
466 }
467
468 bool methodMetaFound;
469 static if (is(typeof(Interface.methodMeta) : TMethodMeta[])) {
470 auto meta = find!`a.name == b`(Interface.methodMeta, methodName);
471 if (!meta.empty) {
472 foreach (e; meta.front.exceptions) {
473 code ~= "Interface." ~ e.type ~ " " ~ e.name ~ ";\n";
474 fieldMetaCodes ~= "TFieldMeta(`" ~ e.name ~ "`, " ~ to!string(e.id) ~
475 ", TReq.OPTIONAL)";
476 }
477 methodMetaFound = true;
478 }
479 }
480
481 version (TVerboseCodegen) {
482 if (!methodMetaFound &&
483 ParameterTypeTuple!(mixin("Interface." ~ methodName)).length > 0)
484 {
485 code ~= "pragma(msg, `[thrift.codegen.processor.TResultStruct] Warning: No " ~
486 "meta information for method '" ~ methodName ~ "' in service '" ~
487 Interface.stringof ~ "' found.`);\n";
488 }
489 }
490
491 immutable fieldMetaCode =
492 fieldMetaCodes.empty ? "" : "[" ~ ctfeJoin(fieldMetaCodes) ~ "]";
493 code ~= "mixin TStructHelpers!(" ~ fieldMetaCode ~ ");\n";
494 code ~= "}\n";
495 return code;
496 }());
497 }