1 /** Automatic high-level RESTful client/server interface generation facilities.
2 
3 	This modules aims to provide a typesafe way to deal with RESTful APIs. D's
4 	`interface`s are used to define the behavior of the API, so that they can
5 	be used transparently within the application. This module assumes that
6 	HTTP is used as the underlying transport for the REST API.
7 
8 	While convenient means are provided for generating both, the server and the
9 	client side, of the API from a single interface definition, it is also
10 	possible to use as a pure client side implementation to target existing
11 	web APIs.
12 
13 	The following paragraphs will explain in detail how the interface definition
14 	is mapped to the RESTful API, without going into specifics about the client
15 	or server side. Take a look at `registerRestInterface` and
16 	`RestInterfaceClient` for more information in those areas.
17 
18 	These are the main adantages of using this module to define RESTful APIs
19 	over defining them manually by registering request handlers in a
20 	`URLRouter`:
21 
22 	$(UL
23 		$(LI Automatic client generation: once the interface is defined, it can
24 			be used both by the client side and the server side, which means
25 			that there is no way to have a protocol mismatch between the two.)
26 		$(LI Automatic route generation for the server: one job of the REST
27 			module is to generate the HTTP routes/endpoints for the API.)
28 		$(LI Automatic serialization/deserialization: Instead of doing manual
29 	  		serialization and deserialization, just normal statically typed
30 	  		member functions are defined and the code generator takes care of
31 	  		converting to/from wire format. Custom serialization can be achieved
32 	  		by defining `JSON` or `string` parameters/return values together
33 	  		with the appropriate `@bodyParam` annotations.)
34 		$(LI Higher level representation integrated into D: Some concepts of the
35 	  		interfaces, such as optional parameters or `in`/`out`/`ref`
36 	  		parameters, as well as `Nullable!T`, are translated naturally to the
37 	  		RESTful protocol.)
38 	)
39 
40 	The most basic interface that can be defined is as follows:
41 	----
42 	@path("/api/")
43 	interface APIRoot {
44 	    string get();
45 	}
46 	----
47 
48 	This defines an API that has a single endpoint, 'GET /api/'. So if the
49 	server is found at http://api.example.com, performing a GET request to
50 	$(CODE http://api.example.com/api/) will call the `get()` method and send
51 	its return value verbatim as the response body.
52 
53 	Endpoint_generation:
54 		An endpoint is a combination of an HTTP method and a local URI. For each
55 		public method of the interface, one endpoint is registered in the
56 		`URLRouter`.
57 
58 		By default, the method and URI parts will be inferred from the method
59 		name by looking for a known prefix. For example, a method called
60 		`getFoo` will automatically be mapped to a 'GET /foo' request. The
61 		recognized prefixes are as follows:
62 
63 		$(TABLE
64 			$(TR $(TH Prefix) $(TH HTTP verb))
65 			$(TR $(TD get)	  $(TD GET))
66 			$(TR $(TD query)  $(TD GET))
67 			$(TR $(TD set)    $(TD PUT))
68 			$(TR $(TD put)    $(TD PUT))
69 			$(TR $(TD update) $(TD PATCH))
70 			$(TR $(TD patch)  $(TD PATCH))
71 			$(TR $(TD add)    $(TD POST))
72 			$(TR $(TD create) $(TD POST))
73 			$(TR $(TD post)   $(TD POST))
74 		)
75 
76 		Member functions that have no valid prefix default to 'POST'. Note that
77 		any of the methods defined in `vibe.http.common.HTTPMethod` are
78 		supported through manual endpoint specifications, as described in the
79 		next section.
80 
81 		After determining the HTTP method, the rest of the method's name is
82 		then treated as the local URI of the endpoint. It is expected to be in
83 		standard D camel case style and will be transformed into the style that
84 		is specified in the call to `registerRestInterface`, which defaults to
85 		`MethodStyle.lowerUnderscored`.
86 
87 	Manual_endpoint_specification:
88 		Endpoints can be controlled manually through the use of `@path` and
89 		`@method` annotations:
90 
91 		----
92 		@path("/api/")
93 		interface APIRoot {
94 		    // Here we use a POST method
95 		    @method(HTTPMethod.POST)
96 			// Our method will located at '/api/foo'
97 			@path("/foo")
98 			void doSomething();
99 		}
100 		----
101 
102 		Manual path annotations also allows defining custom path placeholders
103 		that will be mapped to function parameters. Placeholders are path
104 		segments that start with a colon:
105 
106 		----
107 		@path("/users/")
108 		interface UsersAPI {
109 		    @path(":name")
110 		    Json getUserByName(string _name);
111 		}
112 		----
113 
114 		This will cause a request "GET /users/peter" to be mapped to the
115 		`getUserByName` method, with the `_name` parameter receiving the string
116 		"peter". Note that the matching parameter must have an underscore
117 		prefixed so that it can be distinguished from normal form/query
118 		parameters.
119 
120 		It is possible to partially rely on the default behavior and to only
121 		customize either the method or the path of the endpoint:
122 
123 		----
124 		@method(HTTPMethod.POST)
125 		void getFoo();
126 		----
127 
128 		In the above case, as 'POST' is set explicitly, the route would be
129 		'POST /foo'. On the other hand, if the declaration had been:
130 
131 		----
132 		@path("/bar")
133 		void getFoo();
134 		----
135 
136 		The route generated would be 'GET /bar'.
137 
138 	Properties:
139 		`@property` functions have a special mapping: property getters (no
140 		parameters and a non-void return value) are mapped as GET functions,
141 		and property setters (a single parameter) are mapped as PUT. No prefix
142 		recognition or trimming will be done for properties.
143 
144 	Method_style:
145 		Method names will be translated to the given 'MethodStyle'. The default
146 		style is `MethodStyle.lowerUnderscored`, so that a function named
147 		`getFooBar` will match the route 'GET /foo_bar'. See
148 		`vibe.web.common.MethodStyle` for more information about the available
149 		styles.
150 
151 	Serialization:
152 		By default the return values of the interface methods are serialized
153 		as a JSON text and sent back to the REST client. To override this, you
154 		can use the @resultSerializer attribute
155 
156 		---
157 		struct TestStruct {int i;}
158 
159 		interface IService {
160 		@safe:
161 			@resultSerializer!(
162 				// output_stream implements OutputRange
163 				function (output_stream, test_struct) {
164 					output_stream ~= serializeToJsonString(test_struct);
165 				},
166 				// input_stream implements InputStream
167 				function (input_stream) {
168 					return deserializeJson!TestStruct(input_stream.readAllUTF8());
169 				},
170 				"application/json")()
171 			@resultSerializer!(
172 				// output_stream implements OutputRange
173 				function (output_stream, test_struct) {
174 					output_stream ~= test_struct.i.to!string();
175 				},
176 				// input_stream implements InputStream
177 				function (input_stream) {
178 					TestStruct test_struct;
179 					test_struct.i = input_stream.readAllUTF8().to!int();
180 					return test_struct;
181 				},
182 				"plain/text")()
183 			TestStruct getTest();
184 		}
185 
186 		class Service : IService {
187 		@safe:
188 			TestStruct getTest() {
189 				TestStruct test_struct = {42};
190 				return test_struct;
191 			}
192 		}
193 		---
194 
195 	Serialization_policies:
196 		You can customize the serialization of any type used by an interface
197 		by using serialization policies. The following example is using
198 		the `Base64ArrayPolicy`, which means if `X` contains any ubyte arrays,
199 		they will be serialized to their base64 encoding instead of
200 		their normal string representation (e.g. `"[1, 2, 255]"`).
201 
202 		---
203 		@serializationPolicy!(Base64ArrayPolicy)
204 		interface ITestBase64
205 		{
206 			@safe X getTest();
207 		}
208 		---
209 
210 	Parameters:
211 		Function parameters may be populated from the route, query string,
212 		request body, or headers. They may optionally affect the route URL itself.
213 
214 		By default, parameters are passed differently depending on the type of
215 		request (i.e., HTTP method). For GET and PUT, parameters are passed
216 		via the query string (`<route>?paramA=valueA[?paramB=...]`),
217 		while for POST and PATCH, they are passed via the request body
218 		as a JSON object.
219 
220 		The default behavior can be overridden using one of the following
221 		annotations, put as UDA on the relevant parameter:
222 
223 		$(UL
224 			$(LI `@viaHeader("field")`: Will	source the parameter on which it is
225 				applied from the request headers named "field". If the parameter
226 				is `ref`, it will also be set as a response header. Parameters
227 				declared as `out` will $(I only) be set as a response header.)
228 			$(LI `@viaQuery("field")`: Will source the parameter on which it is
229 				applied from a field named "field" of the query string.)
230 			$(LI `@viaBody("field")`: Will source the parameter on which it is
231 				applied from a field named "field" of the request body
232 				in JSON format, or, if no field is passed, will represent the
233 				whole body. Note that in the later case, there can be no other
234 				`viaBody` parameters.)
235 		)
236 
237 		----
238 		@path("/api/")
239 		interface APIRoot {
240 			// GET /api/header with 'Authorization' set
241 			string getHeader(@viaBody("Authorization") string param);
242 
243 			// GET /api/foo?param=...
244 			string getFoo(@viaQuery("param") int param);
245 
246 			// GET /api/body with body set to { "myFoo": {...} }
247 			string getBody(@viaBody("parameter") FooType myFoo);
248 
249 			// GET /api/full_body with body set to {...}
250 			string getFullBody(@viaBody() FooType myFoo);
251 		}
252 		----
253 
254 		Further, how function parameters are named may affect the route:
255 
256 		$(UL
257 			$(LI $(P Parameters with leading underscores (e.g. `_slug`) are also
258 				interpreted as a route component, but only in the presence of
259 				a `@path` UDA annotation. See Manual endpoint specification above.))
260 			$(LI $(P Other function parameters do not affect or come from the path
261 				 portion of the URL, and are are passed according to the default
262 				 rules above: query string for GET and PUT; request body JSON
263 				 for POST and PATCH.))
264 			$(LI $(P $(B Deprecated:) If the first parameter is named `id`, this is
265 				interpreted as a leading route component. For example,
266 				`getName(int id)` becomes `/:id/name`.)
267 				$(P Note that this style of parameter-based URL routing is
268 				different than in many other web frameworks, where instead
269 				this example would be routed as `/name/:id`.)
270 				$(P See `Collection` for the preferred way to represent object
271 				collections in REST interfaces))
272 		)
273 
274 
275 	Default_values:
276 		Parameters with default values behave as optional parameters. If one is
277 		set in the interface declaration of a method, the client can omit a
278 		value for the corresponding field in the request and the default value
279 		is used instead.
280 
281 		Note that if default parameters are not evaluable by CTFE, compilation
282 		may fail due to DMD bug #14369 (Vibe.d tracking issue: #1043).
283 
284 	Aggregates:
285 		When passing aggregates as parameters, those are serialized differently
286 		depending on the way they are passed, which may be especially important
287 		when interfacing with an existing RESTful API:
288 
289 		$(UL
290 			$(LI If the parameter is passed via the headers or the query, either
291 				implicitly or explicitly, the aggregate is serialized to JSON.
292 				If the JSON representation is a single string, the string value
293 				will be used verbatim. Otherwise the JSON representation will be
294 				used)
295 			$(LI If the parameter is passed via the body, the datastructure is
296 				serialized to JSON and set as a field of the main JSON object
297 				that is expected in the request body. Its field name equals the
298 				parameter name, unless an explicit `@bodyParam` annotation is
299 				used.)
300 		)
301 
302 	See_Also:
303 		To see how to implement the server side in detail, jump to
304 		`registerRestInterface`.
305 
306 		To see how to implement the client side in detail, jump to
307 		the `RestInterfaceClient` documentation.
308 
309 	Copyright: © 2012-2018 Sönke Ludwig
310 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
311 	Authors: Sönke Ludwig, Михаил Страшун, Mathias 'Geod24' Lang
312 */
313 module vibe.web.rest;
314 
315 public import vibe.web.common;
316 
317 import vibe.core.log;
318 import vibe.core.stream : InputStream, isInputStream, pipe;
319 import vibe.http.router : URLRouter;
320 import vibe.http.client : HTTPClientResponse, HTTPClientSettings;
321 import vibe.http.common : HTTPMethod;
322 import vibe.http.server : HTTPServerRequestDelegate, HTTPServerRequest, HTTPServerResponse;
323 import vibe.http.status : HTTPStatus, isSuccessCode;
324 import vibe.internal.meta.uda;
325 import vibe.internal.meta.funcattr;
326 import vibe.inet.url;
327 import vibe.inet.message : InetHeaderMap;
328 import vibe.web.internal.rest.common : RestInterface, Route, SubInterfaceType;
329 import vibe.web.auth : AuthInfo, handleAuthentication, handleAuthorization, isAuthenticated;
330 
331 import std.algorithm : count, startsWith, endsWith, sort, splitter;
332 import std.array : appender, split;
333 import std.meta : AliasSeq;
334 import std.range : isOutputRange;
335 import std.string : strip, indexOf, toLower;
336 import std.typecons : No, Nullable, Yes;
337 import std.typetuple : anySatisfy, Filter;
338 import std.traits;
339 
340 /** Registers a server matching a certain REST interface.
341 
342 	Servers are implementation of the D interface that defines the RESTful API.
343 	The methods of this class are invoked by the code that is generated for
344 	each endpoint of the API, with parameters and return values being translated
345 	according to the rules documented in the `vibe.web.rest` module
346 	documentation.
347 
348 	A basic 'hello world' API can be defined as follows:
349 	----
350 	@path("/api/")
351 	interface APIRoot {
352 	    string get();
353 	}
354 
355 	class API : APIRoot {
356 	    override string get() { return "Hello, World"; }
357 	}
358 
359 	void main()
360 	{
361 	    // -- Where the magic happens --
362 	    router.registerRestInterface(new API());
363 	    // GET http://127.0.0.1:8080/api/ and 'Hello, World' will be replied
364 	    listenHTTP("127.0.0.1:8080", router);
365 
366 	    runApplication();
367 	}
368 	----
369 
370 	As can be seen here, the RESTful logic can be written inside the class
371 	without any concern for the actual HTTP representation.
372 
373 	Return_value:
374 		By default, all methods that return a value send a 200 (OK) status code,
375 		or 204 if no value is being returned for the body.
376 
377 	Non-success:
378 		In the cases where an error code should be signaled to the user, a
379 		`HTTPStatusException` can be thrown from within the method. It will be
380 		turned into a JSON object that has a `statusMessage` field with the
381 		exception message. In case of other exception types being thrown, the
382 		status code will be set to 500 (internal server error), the
383 		`statusMessage` field will again contain the exception's message, and,
384 		in debug mode, an additional `statusDebugMessage` field will be set to
385 		the complete string representation of the exception
386 		(`Exception.toString`), which usually contains a stack trace useful for
387 		debugging.
388 
389 	Returning_data:
390 		To return data, it is possible to either use the return value, which
391 		will be sent as the response body, or individual `ref`/`out` parameters
392 		can be used. The way they are represented in the response can be
393 		customized by adding `@viaBody`/`@viaHeader` annotations on the
394 		parameter declaration of the method within the interface.
395 
396 		In case of errors, any `@viaHeader` parameters are guaranteed to
397 		be set in the response, so that applications such as HTTP basic
398 		authentication can be implemented.
399 
400 	Template_Params:
401 	    TImpl = Either an interface type, or a class that derives from an
402 			      interface. If the class derives from multiple interfaces,
403 	            the first one will be assumed to be the API description
404 	            and a warning will be issued.
405 
406 	Params:
407 	    router   = The HTTP router on which the interface will be registered
408 	    instance = Server instance to use
409 	    settings = Additional settings, such as the `MethodStyle` or the prefix
410 
411 	See_Also:
412 		`RestInterfaceClient` class for an automated way to generate the
413 		matching client-side implementation.
414 */
415 URLRouter registerRestInterface(TImpl)(URLRouter router, TImpl instance, RestInterfaceSettings settings = null)
416 {
417 	import std.algorithm : filter, map, all;
418 	import std.array : array;
419 	import std.range : front;
420 	import vibe.web.internal.rest.common : ParameterKind;
421 
422 	auto intf = RestInterface!TImpl(settings, false);
423 
424 	foreach (i, ovrld; intf.SubInterfaceFunctions) {
425 		enum fname = __traits(identifier, intf.SubInterfaceFunctions[i]);
426 		alias R = ReturnType!ovrld;
427 
428 		static if (isInstanceOf!(Collection, R)) {
429 			auto ret = __traits(getMember, instance, fname)(R.ParentIDs.init);
430 			router.registerRestInterface!(R.Interface)(ret.m_interface, intf.subInterfaces[i].settings);
431 		} else {
432 			auto ret = __traits(getMember, instance, fname)();
433 			router.registerRestInterface!R(ret, intf.subInterfaces[i].settings);
434 		}
435 	}
436 
437 
438 	foreach (i, func; intf.RouteFunctions) {
439 		auto route = intf.routes[i];
440 
441 		// normal handler
442 		auto handler = jsonMethodHandler!(func, i)(instance, intf);
443 
444 		auto diagparams = route.parameters.filter!(p => p.kind != ParameterKind.internal).map!(p => p.fieldName).array;
445 		logDiagnostic("REST route: %s %s %s", route.method, route.fullPattern, diagparams);
446 		router.match(route.method, route.fullPattern, handler);
447 	}
448 
449 	// here we filter our already existing OPTIONS routes, so we don't overwrite whenever the user explicitly made his own OPTIONS route
450 	auto routesGroupedByPattern = intf.getRoutesGroupedByPattern.filter!(rs => rs.all!(r => r.method != HTTPMethod.OPTIONS));
451 
452 	foreach(routes; routesGroupedByPattern){
453 		auto route = routes.front;
454 		auto handler = optionsMethodHandler(routes, settings);
455 
456 		auto diagparams = route.parameters.filter!(p => p.kind != ParameterKind.internal).map!(p => p.fieldName).array;
457 		logDiagnostic("REST route: %s %s %s", HTTPMethod.OPTIONS, route.fullPattern, diagparams);
458 		router.match(HTTPMethod.OPTIONS, route.fullPattern, handler);
459 	}
460 	return router;
461 }
462 
463 /// ditto
464 URLRouter registerRestInterface(TImpl)(URLRouter router, TImpl instance, MethodStyle style)
465 {
466 	return registerRestInterface(router, instance, "/", style);
467 }
468 
469 /// ditto
470 URLRouter registerRestInterface(TImpl)(URLRouter router, TImpl instance, string url_prefix,
471 	MethodStyle style = MethodStyle.lowerUnderscored)
472 {
473 	auto settings = new RestInterfaceSettings;
474 	if (!url_prefix.startsWith("/")) url_prefix = "/"~url_prefix;
475 	settings.baseURL = URL("http://127.0.0.1"~url_prefix);
476 	settings.methodStyle = style;
477 	return registerRestInterface(router, instance, settings);
478 }
479 
480 
481 /**
482 	This is a very limited example of REST interface features. Please refer to
483 	the "rest" project in the "examples" folder for a full overview.
484 
485 	All details related to HTTP are inferred from the interface declaration.
486 */
487 @safe unittest
488 {
489 	@path("/")
490 	interface IMyAPI
491 	{
492 		@safe:
493 		// GET /api/greeting
494 		@property string greeting();
495 
496 		// PUT /api/greeting
497 		@property void greeting(string text);
498 
499 		// POST /api/users
500 		@path("/users")
501 		void addNewUser(string name);
502 
503 		// GET /api/users
504 		@property string[] users();
505 
506 		// GET /api/:id/name
507 		string getName(int id);
508 
509 		// GET /some_custom_json
510 		Json getSomeCustomJson();
511 	}
512 
513 	// vibe.d takes care of all JSON encoding/decoding
514 	// and actual API implementation can work directly
515 	// with native types
516 
517 	class API : IMyAPI
518 	{
519 		private {
520 			string m_greeting;
521 			string[] m_users;
522 		}
523 
524 		@property string greeting() { return m_greeting; }
525 		@property void greeting(string text) { m_greeting = text; }
526 
527 		void addNewUser(string name) { m_users ~= name; }
528 
529 		@property string[] users() { return m_users; }
530 
531 		string getName(int id) { return m_users[id]; }
532 
533 		Json getSomeCustomJson()
534 		{
535 			Json ret = Json.emptyObject;
536 			ret["somefield"] = "Hello, World!";
537 			return ret;
538 		}
539 	}
540 
541 	// actual usage, this is usually done in app.d module
542 	// constructor
543 
544 	void static_this()
545 	{
546 		import vibe.http.server, vibe.http.router;
547 
548 		auto router = new URLRouter;
549 		router.registerRestInterface(new API());
550 		listenHTTP(new HTTPServerSettings(), router);
551 	}
552 }
553 
554 
555 /**
556 	Returns a HTTP handler delegate that serves a JavaScript REST client.
557 */
558 HTTPServerRequestDelegate serveRestJSClient(I)(RestInterfaceSettings settings)
559 	if (is(I == interface))
560 {
561 	import std.datetime.systime : SysTime;
562 	import std.array : appender;
563 
564 	import vibe.http.fileserver : ETag, handleCache;
565 
566 	auto app = appender!string();
567 	generateRestJSClient!I(app, settings);
568 	ETag tag = ETag.md5(No.weak, app.data);
569 
570 	void serve(HTTPServerRequest req, HTTPServerResponse res)
571 	{
572 		if (handleCache(req, res, tag, SysTime.init, "public"))
573 			return;
574 
575 		res.writeBody(app.data, "application/javascript; charset=UTF-8");
576 	}
577 
578 	return &serve;
579 }
580 /// ditto
581 HTTPServerRequestDelegate serveRestJSClient(I)(URL base_url)
582 {
583 	auto settings = new RestInterfaceSettings;
584 	settings.baseURL = base_url;
585 	return serveRestJSClient!I(settings);
586 }
587 /// ditto
588 HTTPServerRequestDelegate serveRestJSClient(I)(string base_url)
589 {
590 	auto settings = new RestInterfaceSettings;
591 	settings.baseURL = URL(base_url);
592 	return serveRestJSClient!I(settings);
593 }
594 /// ditto
595 HTTPServerRequestDelegate serveRestJSClient(I)()
596 {
597 	auto settings = new RestInterfaceSettings;
598 	return serveRestJSClient!I(settings);
599 }
600 
601 ///
602 unittest {
603 	import vibe.http.server;
604 
605 	interface MyAPI {
606 		string getFoo();
607 		void postBar(string param);
608 	}
609 
610 	void test()
611 	{
612 		auto restsettings = new RestInterfaceSettings;
613 		restsettings.baseURL = URL("http://api.example.org/");
614 
615 		auto router = new URLRouter;
616 		router.get("/myapi.js", serveRestJSClient!MyAPI(restsettings));
617 		//router.get("/myapi.js", serveRestJSClient!MyAPI(URL("http://api.example.org/")));
618 		//router.get("/myapi.js", serveRestJSClient!MyAPI("http://api.example.org/"));
619 		//router.get("/myapi.js", serveRestJSClient!MyAPI()); // if want to request to self server
620 		//router.get("/", staticTemplate!"index.dt");
621 
622 		listenHTTP(new HTTPServerSettings, router);
623 	}
624 
625 	/*
626 		index.dt:
627 		html
628 			head
629 				title JS REST client test
630 				script(src="myapi.js")
631 			body
632 				button(onclick="MyAPI.postBar('hello');")
633 	*/
634 }
635 
636 
637 /**
638 	Generates JavaScript code to access a REST interface from the browser.
639 */
640 void generateRestJSClient(I, R)(ref R output, RestInterfaceSettings settings = null)
641 	if (is(I == interface) && isOutputRange!(R, char))
642 {
643 	import vibe.web.internal.rest.jsclient : generateInterface, JSRestClientSettings;
644 	auto jsgenset = new JSRestClientSettings;
645 	output.generateInterface!I(settings, jsgenset, true);
646 }
647 
648 /// Writes a JavaScript REST client to a local .js file.
649 unittest {
650 	import vibe.core.file;
651 
652 	interface MyAPI {
653 		void getFoo();
654 		void postBar(string param);
655 	}
656 
657 	void generateJSClientImpl()
658 	{
659 		import std.array : appender;
660 
661 		auto app = appender!string;
662 		auto settings = new RestInterfaceSettings;
663 		settings.baseURL = URL("http://localhost/");
664 		generateRestJSClient!MyAPI(app, settings);
665 	}
666 
667 	generateJSClientImpl();
668 }
669 
670 
671 /**
672 	Implements the given interface by forwarding all public methods to a REST server.
673 
674 	The server must talk the same protocol as registerRestInterface() generates. Be sure to set
675 	the matching method style for this. The RestInterfaceClient class will derive from the
676 	interface that is passed as a template argument. It can be used as a drop-in replacement
677 	of the real implementation of the API this way.
678 
679 	Non-success:
680 		If a request failed, timed out, or the server returned an non-success status code,
681 		an `vibe.web.common.RestException` will be thrown.
682 */
683 class RestInterfaceClient(I) : I
684 {
685 	import vibe.inet.url : URL;
686 	import vibe.http.client : HTTPClientRequest;
687 	import std.typetuple : staticMap;
688 
689 	private alias Info = RestInterface!I;
690 
691 	//pragma(msg, "imports for "~I.stringof~":");
692 	//pragma(msg, generateModuleImports!(I)());
693 	mixin(generateModuleImports!I());
694 
695 	private {
696 		// storing this struct directly causes a segfault when built with
697 		// LDC 0.15.x, so we are using a pointer here:
698 		RestInterface!I* m_intf;
699 		RequestFilter m_requestFilter;
700 		RequestBodyFilter m_requestBodyFilter;
701 		staticMap!(RestInterfaceClient, Info.SubInterfaceTypes) m_subInterfaces;
702 	}
703 
704 	alias RequestFilter = void delegate(HTTPClientRequest req) @safe;
705 
706 	alias RequestBodyFilter = void delegate(HTTPClientRequest req, scope InputStream body_contents) @safe;
707 
708 	/**
709 		Creates a new REST client implementation of $(D I).
710 	*/
711 	this(RestInterfaceSettings settings)
712 	{
713 		m_intf = new Info(settings, true);
714 
715 		foreach (i, SI; Info.SubInterfaceTypes)
716 			m_subInterfaces[i] = new RestInterfaceClient!SI(m_intf.subInterfaces[i].settings);
717 	}
718 
719 	/// ditto
720 	this(string base_url, MethodStyle style = MethodStyle.lowerUnderscored)
721 	{
722 		this(URL(base_url), style);
723 	}
724 
725 	/// ditto
726 	this(URL base_url, MethodStyle style = MethodStyle.lowerUnderscored)
727 	{
728 		scope settings = new RestInterfaceSettings;
729 		settings.baseURL = base_url;
730 		settings.methodStyle = style;
731 		this(settings);
732 	}
733 
734 	/**
735 		An optional request filter that allows to modify each request before it is made.
736 	*/
737 	final @property RequestFilter requestFilter()
738 	{
739 		return m_requestFilter;
740 	}
741 	/// ditto
742 	final @property void requestFilter(RequestFilter v)
743 	{
744 		m_requestFilter = v;
745 		foreach (i, SI; Info.SubInterfaceTypes)
746 			m_subInterfaces[i].requestFilter = v;
747 	}
748 	/// ditto
749 	final @property void requestFilter(void delegate(HTTPClientRequest req) v)
750 	{
751 		this.requestFilter = cast(RequestFilter)v;
752 	}
753 
754 	/** Optional request filter with access to the request body.
755 
756 		This callback allows to modify the request headers depending on the
757 		contents of the body.
758 	*/
759 	final @property void requestBodyFilter(RequestBodyFilter del)
760 	{
761 		m_requestBodyFilter = del;
762 	}
763 	/// ditto
764 	final @property RequestBodyFilter requestBodyFilter()
765 	{
766 		return m_requestBodyFilter;
767 	}
768 
769 	//pragma(msg, "restinterface:");
770 	mixin(generateRestClientMethods!I());
771 
772 	protected {
773 		import vibe.data.json : Json;
774 		import vibe.textfilter.urlencode;
775 
776 		/**
777 		 * Perform a request to the interface using the given parameters.
778 		 *
779 		 * Params:
780 		 * verb = Kind of request (See $(D HTTPMethod) enum).
781 		 * name = Location to request. For a request on https://github.com/rejectedsoftware/vibe.d/issues?q=author%3ASantaClaus,
782 		 *		it will be '/rejectedsoftware/vibe.d/issues'.
783 		 * hdrs = The headers to send. Some field might be overriden (such as Content-Length). However, Content-Type will NOT be overriden.
784 		 * query = The $(B encoded) query string. For a request on https://github.com/rejectedsoftware/vibe.d/issues?q=author%3ASantaClaus,
785 		 *		it will be 'author%3ASantaClaus'.
786 		 * body_ = The body to send, as a string. If a Content-Type is present in $(D hdrs), it will be used, otherwise it will default to
787 		 *		the generic type "application/json".
788 		 * reqReturnHdrs = A map of required return headers.
789 		 *				   To avoid returning unused headers, nothing is written
790 		 *				   to this structure unless there's an (usually empty)
791 		 *				   entry (= the key exists) with the same key.
792 		 *				   If any key present in `reqReturnHdrs` is not present
793 		 *				   in the response, an Exception is thrown.
794 		 * optReturnHdrs = A map of optional return headers.
795 		 *				   This behaves almost as exactly as reqReturnHdrs,
796 		 *				   except that non-existent key in the response will
797 		 *				   not cause it to throw, but rather to set this entry
798 		 *				   to 'null'.
799 		 *
800 		 * Returns:
801 		 *     The Json object returned by the request
802 		 */
803 		Json request(HTTPMethod verb, string name,
804 					 const scope ref InetHeaderMap hdrs, string query, string body_,
805 					 ref InetHeaderMap reqReturnHdrs,
806 					 ref InetHeaderMap optReturnHdrs) const
807 		{
808 			auto path = URL(m_intf.baseURL).pathString;
809 
810 			if (name.length)
811 			{
812 				if (path.length && path[$ - 1] == '/' && name[0] == '/')
813 					path ~= name[1 .. $];
814 				else if (path.length && path[$ - 1] == '/' || name[0] == '/')
815 					path ~= name;
816 				else
817 					path ~= '/' ~ name;
818 			}
819 
820 			auto httpsettings = m_intf.settings.httpClientSettings;
821 
822 			auto http_resp = .request(URL(m_intf.baseURL), m_requestFilter,
823 				m_requestBodyFilter, verb, path,
824 				hdrs, query, body_, reqReturnHdrs, optReturnHdrs, httpsettings);
825 			scope(exit) http_resp.dropBody();
826 
827 			return http_resp.readJson();
828 		}
829 	}
830 }
831 
832 ///
833 unittest
834 {
835 	interface IMyApi
836 	{
837 		// GET /status
838 		string getStatus();
839 
840 		// GET /greeting
841 		@property string greeting();
842 		// PUT /greeting
843 		@property void greeting(string text);
844 
845 		// POST /new_user
846 		void addNewUser(string name);
847 		// GET /users
848 		@property string[] users();
849 		// GET /:id/name
850 		string getName(int id);
851 
852 		Json getSomeCustomJson();
853 	}
854 
855 	void test()
856 	{
857 		auto api = new RestInterfaceClient!IMyApi("http://127.0.0.1/api/");
858 
859 		logInfo("Status: %s", api.getStatus());
860 		api.greeting = "Hello, World!";
861 		logInfo("Greeting message: %s", api.greeting);
862 		api.addNewUser("Peter");
863 		api.addNewUser("Igor");
864 		logInfo("Users: %s", api.users);
865 		logInfo("First user name: %s", api.getName(0));
866 	}
867 }
868 
869 
870 /**
871 	Encapsulates settings used to customize the generated REST interface.
872 */
873 class RestInterfaceSettings {
874 	/** The public URL below which the REST interface is registered.
875 	*/
876 	URL baseURL;
877 
878 	/** List of allowed origins for CORS
879 
880 		Empty list is interpreted as allowing all origins (e.g. *)
881 	*/
882 	string[] allowedOrigins;
883 
884 	/** Naming convention used for the generated URLs.
885 	*/
886 	MethodStyle methodStyle = MethodStyle.lowerUnderscored;
887 
888     /** The content type the client would like to receive the data back
889 	*/
890 	string content_type = "application/json";
891 
892 	/** Ignores a trailing underscore in method and function names.
893 
894 		With this setting set to $(D true), it's possible to use names in the
895 		REST interface that are reserved words in D.
896 	*/
897 	bool stripTrailingUnderscore = true;
898 
899 	/// Overrides the default HTTP client settings used by the `RestInterfaceClient`.
900 	HTTPClientSettings httpClientSettings;
901 
902 	/** Optional handler used to render custom replies in case of errors.
903 
904 		The handler needs to set the response status code to the provided
905 		`RestErrorInformation.statusCode` value and can then write a custom
906 		response body.
907 
908 		Note that the REST interface generator by default handles any exceptions thrown
909 		during request handling and sents a JSON response with the error message. The
910 		low level `HTTPServerSettings.errorPageHandler` is not invoked.
911 
912 		If `errorHandler` is not set, a JSON object with a single field "statusMessage"
913 		will be sent. In debug builds, there may also be an additional
914 		"statusDebugMessage" field that contains the full exception text, including a
915 		possible stack trace.
916 	*/
917 	RestErrorHandler errorHandler;
918 
919 	@property RestInterfaceSettings dup()
920 	const @safe {
921 		auto ret = new RestInterfaceSettings;
922 		ret.baseURL = this.baseURL;
923 		ret.methodStyle = this.methodStyle;
924 		ret.stripTrailingUnderscore = this.stripTrailingUnderscore;
925 		ret.allowedOrigins = this.allowedOrigins.dup;
926 		ret.content_type = this.content_type.dup;
927 		ret.errorHandler = this.errorHandler;
928 		if (this.httpClientSettings) {
929 			ret.httpClientSettings = this.httpClientSettings.dup;
930 		}
931 		return ret;
932 	}
933 }
934 
935 alias RestErrorHandler = void delegate(HTTPServerRequest, HTTPServerResponse, RestErrorInformation error) @safe;
936 
937 struct RestErrorInformation {
938 	/// The status code that the handler should send in the reply
939 	HTTPStatus statusCode;
940 
941 	/** If triggered by an exception, this contains the catched exception
942 		object.
943 	*/
944 	Exception exception;
945 
946 	private this(Exception e, HTTPStatus default_status)
947 	@safe {
948 		import vibe.http.common : HTTPStatusException;
949 
950 		this.exception = e;
951 
952 		if (auto he = cast(HTTPStatusException)e) {
953 			this.statusCode = cast(HTTPStatus)he.status;
954 		} else {
955 			this.statusCode = default_status;
956 		}
957 	}
958 }
959 
960 
961 /**
962 	Models REST collection interfaces using natural D syntax.
963 
964 	Use this type as the return value of a REST interface getter method/property
965 	to model a collection of objects. `opIndex` is used to make the individual
966 	entries accessible using the `[index]` syntax. Nested collections are
967 	supported.
968 
969 	The interface `I` needs to define a struct named `CollectionIndices`. The
970 	members of this struct denote the types and names of the indexes that lead
971 	to a particular resource. If a collection is nested within another
972 	collection, the order of these members must match the nesting order
973 	(outermost first).
974 
975 	The parameter list of all of `I`'s methods must begin with all but the last
976 	entry in `CollectionIndices`. Methods that also match the last entry will be
977 	considered methods of a collection item (`collection[index].method()`),
978 	wheres all other methods will be considered methods of the collection
979 	itself (`collection.method()`).
980 
981 	The name of the index parameters affects the default path of a method's
982 	route. Normal parameter names will be subject to the same rules as usual
983 	routes (see `registerRestInterface`) and will be mapped to query or form
984 	parameters at the protocol level. Names starting with an underscore will
985 	instead be mapped to path placeholders. For example,
986 	`void getName(int __item_id)` will be mapped to a GET request to the
987 	path `":item_id/name"`.
988 */
989 struct Collection(I)
990 	if (is(I == interface))
991 {
992 	import std.typetuple;
993 
994 	static assert(is(I.CollectionIndices == struct), "Collection interfaces must define a CollectionIndices struct.");
995 
996 	alias Interface = I;
997 	alias AllIDs = TypeTuple!(typeof(I.CollectionIndices.tupleof));
998 	alias AllIDNames = FieldNameTuple!(I.CollectionIndices);
999 	static assert(AllIDs.length >= 1, I.stringof~".CollectionIndices must define at least one member.");
1000 	static assert(AllIDNames.length == AllIDs.length);
1001 	alias ItemID = AllIDs[$-1];
1002 	alias ParentIDs = AllIDs[0 .. $-1];
1003 	alias ParentIDNames = AllIDNames[0 .. $-1];
1004 
1005 	private {
1006 		I m_interface;
1007 		ParentIDs m_parentIDs;
1008 	}
1009 
1010 	/** Constructs a new collection instance that is tied to a particular
1011 		parent collection entry.
1012 
1013 		Params:
1014 			api = The target interface imstance to be mapped as a collection
1015 			pids = The indexes of all collections in which this collection is
1016 				nested (if any)
1017 	*/
1018 	this(I api, ParentIDs pids)
1019 	{
1020 		m_interface = api;
1021 		m_parentIDs = pids;
1022 	}
1023 
1024 	static struct Item {
1025 		private {
1026 			I m_interface;
1027 			AllIDs m_id;
1028 		}
1029 
1030 		this(I api, AllIDs id)
1031 		{
1032 			m_interface = api;
1033 			m_id = id;
1034 		}
1035 
1036 		// forward all item methods
1037 		mixin(() {
1038 			string ret;
1039 			foreach (m; __traits(allMembers, I)) {
1040 				foreach (ovrld; MemberFunctionsTuple!(I, m)) {
1041 					alias PT = ParameterTypeTuple!ovrld;
1042 					static if (matchesAllIDs!ovrld)
1043 						ret ~= "auto "~m~"(ARGS...)(ARGS args) { return m_interface."~m~"(m_id, args); }\n";
1044 				}
1045 			}
1046 			return ret;
1047 		} ());
1048 	}
1049 
1050 	// Note: the example causes a recursive template instantiation if done as a documented unit test:
1051 	/** Accesses a single collection entry.
1052 
1053 		Example:
1054 		---
1055 		interface IMain {
1056 			@property Collection!IItem items();
1057 		}
1058 
1059 		interface IItem {
1060 			struct CollectionIndices {
1061 				int _itemID;
1062 			}
1063 
1064 			@method(HTTPMethod.GET)
1065 			string name(int _itemID);
1066 		}
1067 
1068 		void test(IMain main)
1069 		{
1070 			auto item_name = main.items[23].name; // equivalent to IItem.name(23)
1071 		}
1072 		---
1073 	*/
1074 	Item opIndex(ItemID id)
1075 	{
1076 		return Item(m_interface, m_parentIDs, id);
1077 	}
1078 
1079 	// forward all non-item methods
1080 	mixin(() {
1081 		string ret;
1082 		foreach (m; __traits(allMembers, I)) {
1083 			foreach (ovrld; MemberFunctionsTuple!(I, m)) {
1084 				alias PT = ParameterTypeTuple!ovrld;
1085 				static if (!matchesAllIDs!ovrld && !hasUDA!(ovrld, NoRouteAttribute)) {
1086 					static assert(matchesParentIDs!ovrld,
1087 						"Collection methods must take all parent IDs as the first parameters."~PT.stringof~"   "~ParentIDs.stringof);
1088 					ret ~= "auto "~m~"(ARGS...)(ARGS args) { return m_interface."~m~"(m_parentIDs, args); }\n";
1089 				}
1090 			}
1091 		}
1092 		return ret;
1093 	} ());
1094 
1095 	private template matchesParentIDs(alias func) {
1096 		static if (is(ParameterTypeTuple!func[0 .. ParentIDs.length] == ParentIDs)) {
1097 			static if (ParentIDNames.length == 0) enum matchesParentIDs = true;
1098 			else static if (ParameterIdentifierTuple!func[0 .. ParentIDNames.length] == ParentIDNames)
1099 				enum matchesParentIDs = true;
1100 			else enum matchesParentIDs = false;
1101 		} else enum matchesParentIDs = false;
1102 	}
1103 
1104 	private template matchesAllIDs(alias func) {
1105 		static if (is(ParameterTypeTuple!func[0 .. AllIDs.length] == AllIDs)) {
1106 			static if (ParameterIdentifierTuple!func[0 .. AllIDNames.length] == AllIDNames)
1107 				enum matchesAllIDs = true;
1108 			else enum matchesAllIDs = false;
1109 		} else enum matchesAllIDs = false;
1110 	}
1111 }
1112 
1113 /// Model two nested collections using path based indexes
1114 unittest {
1115 	//
1116 	// API definition
1117 	//
1118 	interface SubItemAPI {
1119 		// Define the index path that leads to a sub item
1120 		struct CollectionIndices {
1121 			// The ID of the base item. This must match the definition in
1122 			// ItemAPI.CollectionIndices
1123 			string _item;
1124 			// The index if the sub item
1125 			int _index;
1126 		}
1127 
1128 		// GET /items/:item/subItems/length
1129 		@property int length(string _item);
1130 
1131 		// GET /items/:item/subItems/:index/squared_position
1132 		int getSquaredPosition(string _item, int _index);
1133 	}
1134 
1135 	interface ItemAPI {
1136 		// Define the index that identifies an item
1137 		struct CollectionIndices {
1138 			string _item;
1139 		}
1140 
1141 		// base path /items/:item/subItems
1142 		Collection!SubItemAPI subItems(string _item);
1143 
1144 		// GET /items/:item/name
1145 		@property string name(string _item);
1146 	}
1147 
1148 	interface API {
1149 		// a collection of items at the base path /items/
1150 		Collection!ItemAPI items();
1151 	}
1152 
1153 	//
1154 	// Local API implementation
1155 	//
1156 	class SubItemAPIImpl : SubItemAPI {
1157 		@property int length(string _item) { return 10; }
1158 
1159 		int getSquaredPosition(string _item, int _index) { return _index ^^ 2; }
1160 	}
1161 
1162 	class ItemAPIImpl : ItemAPI {
1163 		private SubItemAPIImpl m_subItems;
1164 
1165 		this() { m_subItems = new SubItemAPIImpl; }
1166 
1167 		Collection!SubItemAPI subItems(string _item) { return Collection!SubItemAPI(m_subItems, _item); }
1168 
1169 		string name(string _item) { return _item; }
1170 	}
1171 
1172 	class APIImpl : API {
1173 		private ItemAPIImpl m_items;
1174 
1175 		this() { m_items = new ItemAPIImpl; }
1176 
1177 		Collection!ItemAPI items() { return Collection!ItemAPI(m_items); }
1178 	}
1179 
1180 	//
1181 	// Resulting API usage
1182 	//
1183 	API api = new APIImpl; // A RestInterfaceClient!API would work just as well
1184 
1185 	// GET /items/foo/name
1186 	assert(api.items["foo"].name == "foo");
1187 	// GET /items/foo/sub_items/length
1188 	assert(api.items["foo"].subItems.length == 10);
1189 	// GET /items/foo/sub_items/2/squared_position
1190 	assert(api.items["foo"].subItems[2].getSquaredPosition() == 4);
1191 }
1192 
1193 unittest {
1194 	interface I {
1195 		struct CollectionIndices {
1196 			int id1;
1197 			string id2;
1198 		}
1199 
1200 		void a(int id1, string id2);
1201 		void b(int id1, int id2);
1202 		void c(int id1, string p);
1203 		void d(int id1, string id2, int p);
1204 		void e(int id1, int id2, int p);
1205 		void f(int id1, string p, int q);
1206 	}
1207 
1208 	Collection!I coll;
1209 	static assert(is(typeof(coll["x"].a()) == void));
1210 	static assert(is(typeof(coll.b(42)) == void));
1211 	static assert(is(typeof(coll.c("foo")) == void));
1212 	static assert(is(typeof(coll["x"].d(42)) == void));
1213 	static assert(is(typeof(coll.e(42, 42)) == void));
1214 	static assert(is(typeof(coll.f("foo", 42)) == void));
1215 }
1216 
1217 /// Model two nested collections using normal query parameters as indexes
1218 unittest {
1219 	//
1220 	// API definition
1221 	//
1222 	interface SubItemAPI {
1223 		// Define the index path that leads to a sub item
1224 		struct CollectionIndices {
1225 			// The ID of the base item. This must match the definition in
1226 			// ItemAPI.CollectionIndices
1227 			string item;
1228 			// The index if the sub item
1229 			int index;
1230 		}
1231 
1232 		// GET /items/subItems/length?item=...
1233 		@property int length(string item);
1234 
1235 		// GET /items/subItems/squared_position?item=...&index=...
1236 		int getSquaredPosition(string item, int index);
1237 	}
1238 
1239 	interface ItemAPI {
1240 		// Define the index that identifies an item
1241 		struct CollectionIndices {
1242 			string item;
1243 		}
1244 
1245 		// base path /items/subItems?item=...
1246 		Collection!SubItemAPI subItems(string item);
1247 
1248 		// GET /items/name?item=...
1249 		@property string name(string item);
1250 	}
1251 
1252 	interface API {
1253 		// a collection of items at the base path /items/
1254 		Collection!ItemAPI items();
1255 	}
1256 
1257 	//
1258 	// Local API implementation
1259 	//
1260 	class SubItemAPIImpl : SubItemAPI {
1261 		@property int length(string item) { return 10; }
1262 
1263 		int getSquaredPosition(string item, int index) { return index ^^ 2; }
1264 	}
1265 
1266 	class ItemAPIImpl : ItemAPI {
1267 		private SubItemAPIImpl m_subItems;
1268 
1269 		this() { m_subItems = new SubItemAPIImpl; }
1270 
1271 		Collection!SubItemAPI subItems(string item) { return Collection!SubItemAPI(m_subItems, item); }
1272 
1273 		string name(string item) { return item; }
1274 	}
1275 
1276 	class APIImpl : API {
1277 		private ItemAPIImpl m_items;
1278 
1279 		this() { m_items = new ItemAPIImpl; }
1280 
1281 		Collection!ItemAPI items() { return Collection!ItemAPI(m_items); }
1282 	}
1283 
1284 	//
1285 	// Resulting API usage
1286 	//
1287 	API api = new APIImpl; // A RestInterfaceClient!API would work just as well
1288 
1289 	// GET /items/name?item=foo
1290 	assert(api.items["foo"].name == "foo");
1291 	// GET /items/subitems/length?item=foo
1292 	assert(api.items["foo"].subItems.length == 10);
1293 	// GET /items/subitems/squared_position?item=foo&index=2
1294 	assert(api.items["foo"].subItems[2].getSquaredPosition() == 4);
1295 }
1296 
1297 unittest {
1298 	interface C {
1299 		struct CollectionIndices {
1300 			int _ax;
1301 			int _b;
1302 		}
1303 		void testB(int _ax, int _b);
1304 	}
1305 
1306 	interface B {
1307 		struct CollectionIndices {
1308 			int _a;
1309 		}
1310 		Collection!C c();
1311 		void testA(int _a);
1312 	}
1313 
1314 	interface A {
1315 		Collection!B b();
1316 	}
1317 
1318 	static assert (!is(typeof(A.init.b[1].c[2].testB())));
1319 }
1320 
1321 /** Allows processing the server request/response before the handler method is called.
1322 
1323 	Note that this attribute is only used by `registerRestInterface`, but not
1324 	by the client generators. This attribute expects the name of a parameter that
1325 	will receive its return value.
1326 
1327 	Writing to the response body from within the specified hander function
1328 	causes any further processing of the request to be skipped. In particular,
1329 	the route handler method will not be called.
1330 
1331 	Note:
1332 		The example shows the drawback of this attribute. It generally is a
1333 		leaky abstraction that propagates to the base interface. For this
1334 		reason the use of this attribute is not recommended, unless there is
1335 		no suitable alternative.
1336 */
1337 alias before = vibe.internal.meta.funcattr.before;
1338 
1339 ///
1340 @safe unittest {
1341 	import vibe.http.server : HTTPServerRequest, HTTPServerResponse;
1342 
1343 	interface MyService {
1344 		long getHeaderCount(size_t foo = 0) @safe;
1345 	}
1346 
1347 	static size_t handler(HTTPServerRequest req, HTTPServerResponse res)
1348 	{
1349 		return req.headers.length;
1350 	}
1351 
1352 	class MyServiceImpl : MyService {
1353 		// the "foo" parameter will receive the number of request headers
1354 		@before!handler("foo")
1355 		long getHeaderCount(size_t foo)
1356 		{
1357 			return foo;
1358 		}
1359 	}
1360 
1361 	void test(URLRouter router)
1362 	@safe {
1363 		router.registerRestInterface(new MyServiceImpl);
1364 	}
1365 }
1366 
1367 
1368 /** Allows processing the return value of a handler method and the request/response objects.
1369 
1370 	The value returned by the REST API will be the value returned by the last
1371 	`@after` handler, which allows to post process the results of the handler
1372 	method.
1373 
1374 	Writing to the response body from within the specified handler function
1375 	causes any further processing of the request ot be skipped, including
1376 	any other `@after` annotations and writing the result value.
1377 */
1378 alias after = vibe.internal.meta.funcattr.after;
1379 
1380 ///
1381 @safe unittest {
1382 	import vibe.http.server : HTTPServerRequest, HTTPServerResponse;
1383 
1384 	interface MyService {
1385 		long getMagic() @safe;
1386 	}
1387 
1388 	static long handler(long ret, HTTPServerRequest req, HTTPServerResponse res)
1389 	@safe {
1390 		return ret * 2;
1391 	}
1392 
1393 	class MyServiceImpl : MyService{
1394 		// the result reported by the REST API will be 42
1395 		@after!handler
1396 		long getMagic()
1397 		{
1398 			return 21;
1399 		}
1400 	}
1401 
1402 	void test(URLRouter router)
1403 	@safe {
1404 		router.registerRestInterface(new MyServiceImpl);
1405 	}
1406 }
1407 
1408 /**
1409  * Generate an handler that will wrap the server's method
1410  *
1411  * This function returns an handler, generated at compile time, that
1412  * will deserialize the parameters, pass them to the function implemented
1413  * by the user, and return what it needs to return, be it header parameters
1414  * or body, which is at the moment either a pure string or a Json object.
1415  *
1416  * One thing that makes this method more complex that it needs be is the
1417  * inability for D to attach UDA to parameters. This means we have to roll
1418  * our own implementation, which tries to be as easy to use as possible.
1419  * We'll require the user to give the name of the parameter as a string to
1420  * our UDA. Hopefully, we're also able to detect at compile time if the user
1421  * made a typo of any kind (see $(D genInterfaceValidationError)).
1422  *
1423  * Note:
1424  * Lots of abbreviations are used to ease the code, such as
1425  * PTT (ParameterTypeTuple), WPAT (WebParamAttributeTuple)
1426  * and PWPAT (ParameterWebParamAttributeTuple).
1427  *
1428  * Params:
1429  *	T = type of the object which represent the REST server (user implemented).
1430  *	Func = An alias to the function of $(D T) to wrap.
1431  *
1432  *	inst = REST server on which to call our $(D Func).
1433  *	settings = REST server configuration.
1434  *
1435  * Returns:
1436  *	A delegate suitable to use as an handler for an HTTP request.
1437  */
1438 private HTTPServerRequestDelegate jsonMethodHandler(alias Func, size_t ridx, T)(T inst, ref RestInterface!T intf)
1439 {
1440 	import std.meta : AliasSeq;
1441 	import std.string : format;
1442 	import std.traits : Unqual;
1443 	import vibe.http.common : HTTPStatusException, enforceBadRequest;
1444 	import vibe.utils.string : sanitizeUTF8;
1445 	import vibe.web.internal.rest.common : ParameterKind;
1446 	import vibe.internal.meta.funcattr : IsAttributedParameter, computeAttributedParameterCtx;
1447 	import vibe.internal.meta.traits : derivedMethod;
1448 	import vibe.textfilter.urlencode : urlDecode;
1449 
1450 	enum Method = __traits(identifier, Func);
1451 	// We need mutable types for deserialization
1452 	alias PTypes = staticMap!(Unqual, ParameterTypeTuple!Func);
1453 	alias PDefaults = ParameterDefaultValueTuple!Func;
1454 	alias CFuncRaw = derivedMethod!(T, Func);
1455 	static if (AliasSeq!(CFuncRaw).length > 0) alias CFunc = CFuncRaw;
1456 	else alias CFunc = Func;
1457 	alias RT = ReturnType!(FunctionTypeOf!Func);
1458 	static const sroute = RestInterface!T.staticRoutes[ridx];
1459 	auto route = intf.routes[ridx];
1460 	auto settings = intf.settings;
1461     alias SerPolicyType = SerPolicyT!(RestInterface!T.I).PolicyTemplate;
1462 
1463 	void handler(HTTPServerRequest req, HTTPServerResponse res)
1464 	@safe {
1465 		if (route.bodyParameters.length) {
1466 			/*enforceBadRequest(req.contentType == "application/json",
1467 				"The Content-Type header needs to be set to application/json.");*/
1468 			enforceBadRequest(req.json.type != Json.Type.undefined,
1469 				"The request body does not contain a valid JSON value.");
1470 			enforceBadRequest(req.json.type == Json.Type.object,
1471 				"The request body must contain a JSON object.");
1472 		}
1473 
1474 		void handleException(Exception e, HTTPStatus default_status)
1475 		@safe {
1476 			logDebug("REST handler exception: %s", () @trusted { return e.toString(); } ());
1477 			if (res.headerWritten) {
1478 				logDebug("Response already started. Client will not receive an error code!");
1479 				return;
1480 			}
1481 
1482 			if (settings.errorHandler) {
1483 				settings.errorHandler(req, res, RestErrorInformation(e, default_status));
1484 			} else {
1485 				import std.algorithm : among;
1486 				debug string debugMsg;
1487 
1488 				if (auto se = cast(HTTPStatusException)e)
1489 					res.statusCode = se.status;
1490 				else debug {
1491 					res.statusCode = HTTPStatus.internalServerError;
1492 					debugMsg = () @trusted { return sanitizeUTF8(cast(ubyte[])e.toString()); }();
1493 				}
1494 				else
1495 					res.statusCode = default_status;
1496 
1497 				// All 1xx(informational), 204 (no content), and 304 (not modified) responses MUST NOT include a message-body.
1498 				// See: https://tools.ietf.org/html/rfc2616#section-4.3
1499 				if (res.statusCode < 200 || res.statusCode.among(204, 304)) {
1500 					res.writeVoidBody();
1501 					return;
1502 				}
1503 
1504 				debug {
1505 					if (debugMsg) {
1506 						res.writeJsonBody(["statusMessage": e.msg, "statusDebugMessage": debugMsg]);
1507 						return;
1508 					}
1509 				}
1510 				res.writeJsonBody(["statusMessage": e.msg]);
1511 			}
1512 		}
1513 
1514 		static if (isAuthenticated!(T, Func)) {
1515 			typeof(handleAuthentication!Func(inst, req, res)) auth_info;
1516 
1517 			try auth_info = handleAuthentication!Func(inst, req, res);
1518 			catch (Exception e) {
1519 				handleException(e, HTTPStatus.unauthorized);
1520 				return;
1521 			}
1522 
1523 			if (res.headerWritten) return;
1524 		}
1525 
1526 
1527 		PTypes params;
1528 
1529 		try {
1530 			foreach (i, PT; PTypes) {
1531 				enum sparam = sroute.parameters[i];
1532 
1533 				static if (sparam.isIn) {
1534 					enum pname = sparam.name;
1535 					auto fieldname = route.parameters[i].fieldName;
1536 					static if (isInstanceOf!(Nullable, PT)) PT v;
1537 					else Nullable!PT v;
1538 
1539 					static if (sparam.kind == ParameterKind.auth) {
1540 						v = auth_info;
1541 					} else static if (sparam.kind == ParameterKind.query) {
1542 						if (auto pv = fieldname in req.query)
1543 							v = fromRestString!(PT, SerPolicyType)(*pv);
1544 					} else static if (sparam.kind == ParameterKind.wholeBody) {
1545 						try v = deserializeWithPolicy!(JsonSerializer, SerPolicyType, PT)(req.json);
1546 						catch (JSONException e) enforceBadRequest(false, e.msg);
1547 					} else static if (sparam.kind == ParameterKind.body_) {
1548 						try {
1549 							if (auto pv = fieldname in req.json)
1550 								v = deserializeWithPolicy!(JsonSerializer, SerPolicyType, PT)(*pv);
1551 						} catch (JSONException e)
1552 							enforceBadRequest(false, e.msg);
1553 					} else static if (sparam.kind == ParameterKind.header) {
1554 						if (auto pv = fieldname in req.headers)
1555 							v = fromRestString!(PT, SerPolicyType)(*pv);
1556 					} else static if (sparam.kind == ParameterKind.attributed) {
1557 						static if (!__traits(compiles, () @safe { computeAttributedParameterCtx!(CFunc, pname)(inst, req, res); } ()))
1558 							pragma(msg, "Non-@safe @before evaluators are deprecated - annotate evaluator function for parameter "~pname~" of "~T.stringof~"."~Method~" as @safe.");
1559 						v = () @trusted { return computeAttributedParameterCtx!(CFunc, pname)(inst, req, res); } ();
1560 					} else static if (sparam.kind == ParameterKind.internal) {
1561 						if (auto pv = fieldname in req.params)
1562 							v = fromRestString!(PT, DefaultPolicy)(urlDecode(*pv));
1563 					} else static assert(false, "Unhandled parameter kind.");
1564 
1565 					static if (isInstanceOf!(Nullable, PT)) params[i] = v;
1566 					else if (v.isNull()) {
1567 						static if (!is(PDefaults[i] == void)) params[i] = PDefaults[i];
1568 						else enforceBadRequest(false, "Missing non-optional "~sparam.kind.to!string~" parameter '"~(fieldname.length?fieldname:sparam.name)~"'.");
1569 					} else params[i] = v.get;
1570 				}
1571 			}
1572 		} catch (Exception e) {
1573 			handleException(e, HTTPStatus.badRequest);
1574 			return;
1575 		}
1576 
1577 		static if (isAuthenticated!(T, Func)) {
1578 			try handleAuthorization!(T, Func, params)(auth_info);
1579 			catch (Exception e) {
1580 				handleException(e, HTTPStatus.forbidden);
1581 				return;
1582 			}
1583 		}
1584 
1585 		void handleCors()
1586 		{
1587 			import std.algorithm : any;
1588 			import std.uni : sicmp;
1589 
1590 			if (req.method == HTTPMethod.OPTIONS)
1591 				return;
1592 			auto origin = "Origin" in req.headers;
1593 			if (origin is null)
1594 				return;
1595 
1596 			if (settings.allowedOrigins.length != 0 &&
1597 				!settings.allowedOrigins.any!(org => org.sicmp((*origin)) == 0))
1598 				return;
1599 
1600 			res.headers["Access-Control-Allow-Origin"] = *origin;
1601 			res.headers["Access-Control-Allow-Credentials"] = "true";
1602 		}
1603 		// Anti copy-paste
1604 		void returnHeaders()
1605 		{
1606 			handleCors();
1607 			foreach (i, P; PTypes) {
1608 				static if (sroute.parameters[i].isOut) {
1609 					static assert (sroute.parameters[i].kind == ParameterKind.header);
1610 					static if (isInstanceOf!(Nullable, typeof(params[i]))) {
1611 						if (!params[i].isNull)
1612 							res.headers[route.parameters[i].fieldName] = to!string(params[i]);
1613 					} else {
1614 						res.headers[route.parameters[i].fieldName] = to!string(params[i]);
1615 					}
1616 				}
1617 			}
1618 		}
1619 
1620 		try {
1621 			import vibe.internal.meta.funcattr;
1622 
1623 			static if (!__traits(compiles, () @safe { __traits(getMember, inst, Method)(params); }))
1624 				pragma(msg, "Non-@safe methods are deprecated in REST interfaces - Mark " ~
1625 					T.stringof ~ "." ~ Method ~ " as @safe.");
1626 
1627 			static if (is(RT == void)) {
1628 				// TODO: remove after deprecation period
1629 				() @trusted { __traits(getMember, inst, Method)(params); } ();
1630 				returnHeaders();
1631 				res.writeBody(cast(ubyte[])null);
1632 			} else static if (isInputStream!RT) {
1633 				returnHeaders();
1634 				auto ret = () @trusted {
1635 					return evaluateOutputModifiers!CFunc(
1636 						__traits(getMember, inst, Method)(params), req, res); } ();
1637 				ret.pipe(res.bodyWriter);
1638 			} else {
1639 				// TODO: remove after deprecation period
1640 				static if (!__traits(compiles, () @safe { evaluateOutputModifiers!Func(RT.init, req, res); } ()))
1641 					pragma(msg, "Non-@safe @after evaluators are deprecated - annotate @after evaluator function for " ~
1642 						T.stringof ~ "." ~ Method ~ " as @safe.");
1643 
1644 				auto ret = () @trusted {
1645 					return evaluateOutputModifiers!CFunc(
1646 						__traits(getMember, inst, Method)(params), req, res); } ();
1647 				returnHeaders();
1648 
1649 				string accept_str;
1650 				if (const accept_header = "Accept" in req.headers)
1651 					accept_str = *accept_header;
1652 				alias result_serializers = ResultSerializersT!Func;
1653 				immutable serializer_ind = get_matching_content_type!(result_serializers)(accept_str);
1654 				foreach (i, serializer; result_serializers)
1655 					if (serializer_ind == i) {
1656 						auto serialized_output = appender!(ubyte[]);
1657 						static if (
1658 							__traits(compiles, () @trusted {
1659 								serializer.serialize!(SerPolicyT!(RestInterface!T.I).PolicyTemplate)(serialized_output, ret);
1660 							})
1661 							&& !__traits(compiles, () @safe {
1662 								serializer.serialize!(SerPolicyT!(RestInterface!T.I).PolicyTemplate)(serialized_output, ret);
1663 							}))
1664 						{
1665 							pragma(msg, "Non-@safe serialization of REST return types deprecated - ensure that " ~
1666 								RT.stringof~" is safely serializable.");
1667 						}
1668 						() @trusted {
1669 							serializer.serialize!(SerPolicyT!(RestInterface!T.I).PolicyTemplate)(serialized_output, ret);
1670 						}();
1671 						res.writeBody(serialized_output.data, serializer.contentType);
1672 					}
1673 				res.statusCode = HTTPStatus.notAcceptable; // will trigger RestException on the client side
1674 				res.writeBody(cast(ubyte[])null);
1675 			}
1676 		} catch (Exception e) {
1677 			returnHeaders();
1678 			handleException(e, HTTPStatus.internalServerError);
1679 		}
1680 	}
1681 
1682 	return &handler;
1683 }
1684 
1685 /**
1686  * Generate an handler that will wrap the server's method
1687  *
1688  * This function returns an handler that handles the http OPTIONS method.
1689  *
1690  * It will return the ALLOW header with all the methods on this resource
1691  * And it will handle Preflight CORS.
1692  *
1693  * Params:
1694  *	routes = a range of Routes were each route has the same resource/URI
1695  *				just different method.
1696  *	settings = REST server configuration.
1697  *
1698  * Returns:
1699  *	A delegate suitable to use as an handler for an HTTP request.
1700  */
1701 private HTTPServerRequestDelegate optionsMethodHandler(RouteRange)(RouteRange routes, RestInterfaceSettings settings = null)
1702 {
1703 	import std.algorithm : map, joiner, any;
1704 	import std.conv : text;
1705 	import std.array : array;
1706 	import vibe.http.common : httpMethodString, httpMethodFromString;
1707 	// NOTE: don't know what is better, to keep this in memory, or generate on each request
1708 	auto allow = routes.map!(r => r.method.httpMethodString).joiner(",").text();
1709 	auto methods = routes.map!(r => r.method).array();
1710 
1711 	void handlePreflightedCors(HTTPServerRequest req, HTTPServerResponse res, ref HTTPMethod[] methods, RestInterfaceSettings settings = null)
1712 	{
1713 		import std.algorithm : among;
1714 		import std.uni : sicmp;
1715 
1716 		auto origin = "Origin" in req.headers;
1717 		if (origin is null)
1718 			return;
1719 
1720 		if (settings !is null &&
1721 			settings.allowedOrigins.length != 0 &&
1722 			!settings.allowedOrigins.any!(org => org.sicmp((*origin)) == 0))
1723 			return;
1724 
1725 		auto method = "Access-Control-Request-Method" in req.headers;
1726 		if (method is null)
1727 			return;
1728 
1729 		auto httpMethod = httpMethodFromString(*method);
1730 
1731 		if (!methods.any!(m => m == httpMethod))
1732 			return;
1733 
1734 		res.headers["Access-Control-Allow-Origin"] = *origin;
1735 
1736 		// there is no way to know if the specific resource supports credentials
1737 		// (either cookies, HTTP authentication, or client-side SSL certificates),
1738 		// so we always assume it does
1739 		res.headers["Access-Control-Allow-Credentials"] = "true";
1740 		res.headers["Access-Control-Max-Age"] = "1728000";
1741 		res.headers["Access-Control-Allow-Methods"] = *method;
1742 
1743 		// we have no way to reliably determine what headers the resource allows
1744 		// so we simply copy whatever the client requested
1745 		if (auto headers = "Access-Control-Request-Headers" in req.headers)
1746 			res.headers["Access-Control-Allow-Headers"] = *headers;
1747 	}
1748 
1749 	void handler(HTTPServerRequest req, HTTPServerResponse res)
1750 	{
1751 		// since this is a OPTIONS request, we have to return the ALLOW headers to tell which methods we have
1752 		res.headers["Allow"] = allow;
1753 
1754 		// handle CORS preflighted requests
1755 		handlePreflightedCors(req,res,methods,settings);
1756 
1757 		// NOTE: besides just returning the allowed methods and handling CORS preflighted requests,
1758 		// this would be a nice place to describe what kind of resources are on this route,
1759 		// the params each accepts, the headers, etc... think WSDL but then for REST.
1760 		res.writeBody("");
1761 	}
1762 	return &handler;
1763 }
1764 
1765 private string generateRestClientMethods(I)()
1766 {
1767 	import std.array : join;
1768 	import std.string : format;
1769 	import std.traits : fullyQualifiedName, isInstanceOf;
1770 
1771 	alias Info = RestInterface!I;
1772 
1773 	string ret = q{
1774 		import vibe.internal.meta.codegen : CloneFunction;
1775 	};
1776 
1777 	// generate sub interface methods
1778 	foreach (i, SI; Info.SubInterfaceTypes) {
1779 		alias F = Info.SubInterfaceFunctions[i];
1780 		alias RT = ReturnType!F;
1781 		alias ParamNames = ParameterIdentifierTuple!F;
1782 		static if (ParamNames.length == 0) enum pnames = "";
1783 		else enum pnames = ", " ~ [ParamNames].join(", ");
1784 		static if (isInstanceOf!(Collection, RT)) {
1785 			ret ~= q{
1786 					mixin CloneFunction!(Info.SubInterfaceFunctions[%1$s], q{
1787 						return Collection!(%2$s)(m_subInterfaces[%1$s]%3$s);
1788 					});
1789 				}.format(i, fullyQualifiedName!SI, pnames);
1790 		} else {
1791 			ret ~= q{
1792 					mixin CloneFunction!(Info.SubInterfaceFunctions[%1$s], q{
1793 						return m_subInterfaces[%1$s];
1794 					});
1795 				}.format(i);
1796 		}
1797 	}
1798 
1799 	// generate route methods
1800 	foreach (i, F; Info.RouteFunctions) {
1801 		alias ParamNames = ParameterIdentifierTuple!F;
1802 		static if (ParamNames.length == 0) enum pnames = "";
1803 		else enum pnames = ", " ~ [ParamNames].join(", ");
1804 
1805 		ret ~= q{
1806 				mixin CloneFunction!(Info.RouteFunctions[%1$s], q{
1807 					return executeClientMethod!(I, %1$s%2$s)(*m_intf, m_requestFilter, m_requestBodyFilter);
1808 				});
1809 			}.format(i, pnames);
1810 	}
1811 
1812 	// generate stubs for non-route functions
1813 	static foreach (m; __traits(allMembers, I))
1814 		foreach (i, fun; MemberFunctionsTuple!(I, m))
1815 			static if (hasUDA!(fun, NoRouteAttribute))
1816 				ret ~= q{
1817 					mixin CloneFunction!(MemberFunctionsTuple!(I, "%s")[%s], q{
1818 						assert(false);
1819 					});
1820 				}.format(m, i);
1821 
1822 	return ret;
1823 }
1824 
1825 
1826 private auto executeClientMethod(I, size_t ridx, ARGS...)
1827 	(const scope ref RestInterface!I intf, scope void delegate(HTTPClientRequest) @safe request_filter,
1828 		scope void delegate(HTTPClientRequest, scope InputStream) @safe request_body_filter)
1829 {
1830 	import vibe.web.internal.rest.common : ParameterKind;
1831 	import vibe.stream.operations : readAll;
1832 	import vibe.textfilter.urlencode : filterURLEncode, urlEncode;
1833 	import std.array : appender;
1834 
1835 	alias Info = RestInterface!I;
1836 	alias Func = Info.RouteFunctions[ridx];
1837 	alias RT = ReturnType!Func;
1838 	alias PTT = ParameterTypeTuple!Func;
1839 	alias SerPolicyType = SerPolicyT!I.PolicyTemplate;
1840 	enum sroute = Info.staticRoutes[ridx];
1841 	auto route = intf.routes[ridx];
1842 	auto settings = intf.settings;
1843 
1844 	InetHeaderMap headers;
1845 	InetHeaderMap reqhdrs;
1846 	InetHeaderMap opthdrs;
1847 
1848 	string url_prefix;
1849 
1850 	auto query = appender!string();
1851 	auto jsonBody = Json.emptyObject;
1852 	string body_;
1853 
1854 	void addQueryParam(size_t i)(string name)
1855 	{
1856 		if (query.data.length) query.put('&');
1857 		query.filterURLEncode(name);
1858 		query.put("=");
1859 		static if (is(PT == Json))
1860 			query.filterURLEncode(ARGS[i].toString());
1861 		else
1862 		// Note: CTFE triggers compiler bug here (think we are returning Json, not string).
1863 				query.filterURLEncode(toRestString(
1864 				serializeWithPolicy!(JsonSerializer, SerPolicyType)(ARGS[i])));
1865 	}
1866 
1867 	foreach (i, PT; PTT) {
1868 		enum sparam = sroute.parameters[i];
1869 		auto fieldname = route.parameters[i].fieldName;
1870 		static if (sparam.kind == ParameterKind.query) {
1871 			addQueryParam!i(fieldname);
1872 		} else static if (sparam.kind == ParameterKind.wholeBody) {
1873 			jsonBody = serializeWithPolicy!(JsonSerializer, SerPolicyType)(ARGS[i]);
1874 		} else static if (sparam.kind == ParameterKind.body_) {
1875 			jsonBody[fieldname] = serializeWithPolicy!(JsonSerializer, SerPolicyType)(ARGS[i]);
1876 		} else static if (sparam.kind == ParameterKind.header) {
1877 			// Don't send 'out' parameter, as they should be default init anyway and it might confuse some server
1878 			static if (sparam.isIn) {
1879 				static if (isInstanceOf!(Nullable, PT)) {
1880 					if (!ARGS[i].isNull)
1881 						headers[fieldname] = to!string(ARGS[i]);
1882 				} else headers[fieldname] = to!string(ARGS[i]);
1883 			}
1884 			static if (sparam.isOut) {
1885 				// Optional parameter
1886 				static if (isInstanceOf!(Nullable, PT)) {
1887 					opthdrs[fieldname] = null;
1888 				} else {
1889 					reqhdrs[fieldname] = null;
1890 				}
1891 			}
1892 		}
1893 	}
1894 
1895 	static if (sroute.method == HTTPMethod.GET) {
1896 		assert(jsonBody == Json.emptyObject, "GET request trying to send body parameters.");
1897 	} else {
1898 		debug body_ = jsonBody.toPrettyString();
1899 		else body_ = jsonBody.toString();
1900 	}
1901 
1902 	string url;
1903 	foreach (i, p; route.fullPathParts) {
1904 		if (p.isParameter) {
1905 			switch (p.text) {
1906 				foreach (j, PT; PTT) {
1907 					static if (sroute.parameters[j].name[0] == '_' || sroute.parameters[j].name == "id") {
1908 						case sroute.parameters[j].name:
1909 							url ~= urlEncode(toRestString(serializeToJson(ARGS[j])));
1910 							goto sbrk;
1911 					}
1912 				}
1913 				default: url ~= ":" ~ p.text; break;
1914 			}
1915 			sbrk:;
1916 		} else url ~= p.text;
1917 	}
1918 
1919 	scope (exit) {
1920 		foreach (i, PT; PTT) {
1921 			enum sparam = sroute.parameters[i];
1922 			auto fieldname = route.parameters[i].fieldName;
1923 			static if (sparam.kind == ParameterKind.header) {
1924 				static if (sparam.isOut) {
1925 					static if (isInstanceOf!(Nullable, PT)) {
1926 						ARGS[i] = to!(TemplateArgsOf!PT)(
1927 							opthdrs.get(fieldname, null));
1928 					} else {
1929 						if (auto ptr = fieldname in reqhdrs)
1930 							ARGS[i] = to!PT(*ptr);
1931 					}
1932 				}
1933 			}
1934 		}
1935 	}
1936 
1937 	headers["Accept"] = settings.content_type;
1938 
1939 	// Do not require a `Content-Type` header if no response is expected
1940 	// https://github.com/vibe-d/vibe.d/issues/2521
1941 	static if (!is(RT == void))
1942 		// Don't override it if set from the parameter
1943 		if ("Content-Type" !in opthdrs)
1944 			opthdrs["Content-Type"] = null;
1945 
1946 	auto ret = request(URL(intf.baseURL), request_filter, request_body_filter,
1947 		sroute.method, url, headers, query.data, body_, reqhdrs, opthdrs,
1948 		intf.settings.httpClientSettings);
1949 
1950 	static if (isInputStream!RT) {
1951 		return RT(ret.bodyReader);
1952 	} else static if (!is(RT == void)) {
1953 		scope(exit) ret.dropBody();
1954 
1955 		string content_type;
1956 		if (const hdr = "Content-Type" in opthdrs)
1957 			content_type = *hdr;
1958 		if (!content_type.length)
1959 			content_type = "application/octet-stream";
1960 
1961 		alias result_serializers = ResultSerializersT!Func;
1962 		immutable serializer_ind = get_matching_content_type!(result_serializers)(content_type);
1963 		foreach (i, serializer; result_serializers)
1964 			if (serializer_ind == i) {
1965 				// TODO: The JSON deserialiation code requires a forward range,
1966 				//       but streamInputRange is currently just a bare input
1967 				//       range, so for now we need to read everything into a
1968 				//       buffer instead.
1969 				//import vibe.stream.wrapper : streamInputRange;
1970 				//auto rng = streamInputRange(ret.bodyReader);
1971 				auto rng = ret.bodyReader.readAll();
1972 				return serializer.deserialize!(SerPolicyT!I.PolicyTemplate, RT)(rng);
1973 			}
1974 
1975 		throw new Exception("Unrecognized content type: " ~ content_type);
1976 	} else ret.dropBody();
1977 }
1978 
1979 
1980 import vibe.http.client : HTTPClientRequest;
1981 /**
1982  * Perform a request to the interface using the given parameters.
1983  *
1984  * Params:
1985  * verb = Kind of request (See $(D HTTPMethod) enum).
1986  * name = Location to request. For a request on https://github.com/rejectedsoftware/vibe.d/issues?q=author%3ASantaClaus,
1987  *		it will be '/rejectedsoftware/vibe.d/issues'.
1988  * hdrs = The headers to send. Some field might be overriden (such as Content-Length). However, Content-Type will NOT be overriden.
1989  * query = The $(B encoded) query string. For a request on https://github.com/rejectedsoftware/vibe.d/issues?q=author%3ASantaClaus,
1990  *		it will be 'author%3ASantaClaus'.
1991  * body_ = The body to send, as a string. If a Content-Type is present in $(D hdrs), it will be used, otherwise it will default to
1992  *		the generic type "application/json".
1993  * reqReturnHdrs = A map of required return headers.
1994  *				   To avoid returning unused headers, nothing is written
1995  *				   to this structure unless there's an (usually empty)
1996  *				   entry (= the key exists) with the same key.
1997  *				   If any key present in `reqReturnHdrs` is not present
1998  *				   in the response, an Exception is thrown.
1999  * optReturnHdrs = A map of optional return headers.
2000  *				   This behaves almost as exactly as reqReturnHdrs,
2001  *				   except that non-existent key in the response will
2002  *				   not cause it to throw, but rather to set this entry
2003  *				   to 'null'.
2004  *
2005  * Returns:
2006  *     The Json object returned by the request
2007  */
2008 private HTTPClientResponse request(URL base_url,
2009 	scope void delegate(HTTPClientRequest) @safe request_filter,
2010 	scope void delegate(HTTPClientRequest, scope InputStream) @safe request_body_filter,
2011 	HTTPMethod verb, string path, const scope ref InetHeaderMap hdrs, string query,
2012 	string body_, ref InetHeaderMap reqReturnHdrs,
2013 	ref InetHeaderMap optReturnHdrs, in HTTPClientSettings http_settings)
2014 @safe {
2015 	import std.uni : sicmp;
2016 	import vibe.http.client : HTTPClientRequest, HTTPClientResponse, requestHTTP;
2017 	import vibe.http.common : HTTPStatusException, HTTPStatus, httpMethodString, httpStatusText;
2018 
2019 	URL url = base_url;
2020 	url.pathString = path;
2021 
2022 	if (query.length) url.queryString = query;
2023 
2024 	auto reqdg = (scope HTTPClientRequest req) {
2025 		req.method = verb;
2026 		foreach (k, v; hdrs.byKeyValue)
2027 			req.headers[k] = v;
2028 
2029 		if (request_body_filter) {
2030 			import vibe.stream.memory : createMemoryStream;
2031 			scope str = createMemoryStream(() @trusted { return cast(ubyte[])body_; } (), false);
2032 			request_body_filter(req, str);
2033 		}
2034 
2035 		if (request_filter) request_filter(req);
2036 
2037 		if (body_ != "")
2038 			req.writeBody(cast(const(ubyte)[])body_, hdrs.get("Content-Type", "application/json; charset=UTF-8"));
2039 	};
2040 
2041 	HTTPClientResponse client_res;
2042 	if (http_settings) client_res = requestHTTP(url, reqdg, http_settings);
2043 	else client_res = requestHTTP(url, reqdg);
2044 
2045 	import vibe.stream.operations;
2046 
2047 	logDebug(
2048 			"REST call: %s %s -> %d, %s",
2049 			httpMethodString(verb),
2050 			url.toString(),
2051 			client_res.statusCode,
2052 			client_res.statusPhrase
2053 			);
2054 
2055 	// Get required headers - Don't throw yet
2056 	string[] missingKeys;
2057 	foreach (k, ref v; reqReturnHdrs.byKeyValue)
2058 		if (auto ptr = k in client_res.headers)
2059 			v = (*ptr).idup;
2060 		else
2061 			missingKeys ~= k;
2062 
2063 	// Get optional headers
2064 	foreach (k, ref v; optReturnHdrs.byKeyValue)
2065 		if (auto ptr = k in client_res.headers)
2066 			v = (*ptr).idup;
2067 		else
2068 			v = null;
2069 	if (missingKeys.length)
2070 		throw new Exception(
2071 			"REST interface mismatch: Missing required header field(s): "
2072 			~ missingKeys.to!string);
2073 
2074 	if (!isSuccessCode(cast(HTTPStatus)client_res.statusCode))
2075 	{
2076 		Json msg = Json(["statusMessage": Json(client_res.statusPhrase)]);
2077 		if (client_res.contentType.length)
2078 			if (client_res.contentType.splitter(";").front.strip.sicmp("application/json") == 0)
2079 				msg = client_res.readJson();
2080 		client_res.dropBody();
2081 		throw new RestException(client_res.statusCode, msg);
2082 	}
2083 
2084 	return client_res;
2085 }
2086 
2087 private {
2088 	import vibe.data.json;
2089 	import std.conv : to;
2090 
2091 	string toRestString(Json value)
2092 	@safe {
2093 		switch (value.type) {
2094 			default: return value.toString();
2095 			case Json.Type.Bool: return value.get!bool ? "true" : "false";
2096 			case Json.Type.Int: return to!string(value.get!long);
2097 			case Json.Type.Float: return to!string(value.get!double);
2098 			case Json.Type.String: return value.get!string;
2099 		}
2100 	}
2101 
2102 	T fromRestString(T, alias SerPolicyType = DefaultPolicy)(string value)
2103 	{
2104 		import std.conv : ConvException;
2105 		import std.uuid : UUID, UUIDParsingException;
2106 		import vibe.http.common : HTTPStatusException;
2107 		import vibe.http.status : HTTPStatus;
2108 		try {
2109 			static if (isInstanceOf!(Nullable, T)) return T(fromRestString!(typeof(T.init.get()))(value));
2110 			else static if (is(T == bool)) return value == "1" || value.to!T;
2111 			else static if (is(T : int)) return to!T(value);
2112 			else static if (is(T : double)) return to!T(value); // FIXME: formattedWrite(dst, "%.16g", json.get!double);
2113 			else static if (is(string : T)) return value;
2114 			else static if (__traits(compiles, T.fromISOExtString("hello"))) return T.fromISOExtString(value);
2115 			else static if (__traits(compiles, T.fromString("hello"))) return T.fromString(value);
2116 			else static if (is(T == UUID)) return UUID(value);
2117 			else return deserializeWithPolicy!(JsonStringSerializer!string, SerPolicyType, T)(value);
2118 		} catch (ConvException e) {
2119 			throw new HTTPStatusException(HTTPStatus.badRequest, e.msg);
2120 		} catch (JSONException e) {
2121 			throw new HTTPStatusException(HTTPStatus.badRequest, e.msg);
2122 		} catch (UUIDParsingException e) {
2123 			throw new HTTPStatusException(HTTPStatus.badRequest, e.msg);
2124 		}
2125 	}
2126 
2127 	// Converting from invalid JSON string to aggregate should throw bad request
2128 	unittest {
2129 		import vibe.http.common : HTTPStatusException;
2130 		import vibe.http.status : HTTPStatus;
2131 
2132 		void assertHTTPStatus(E)(lazy E expression, HTTPStatus expectedStatus,
2133 			string file = __FILE__, size_t line = __LINE__)
2134 		{
2135 			import core.exception : AssertError;
2136 			import std.format : format;
2137 
2138 			try
2139 				expression();
2140 			catch (HTTPStatusException e)
2141 			{
2142 				if (e.status != expectedStatus)
2143 					throw new AssertError(format("assertHTTPStatus failed: " ~
2144 						"status expected %d but was %d", expectedStatus, e.status),
2145 						file, line);
2146 
2147 				return;
2148 			}
2149 
2150 			throw new AssertError("assertHTTPStatus failed: No " ~
2151 				"'HTTPStatusException' exception was thrown", file, line);
2152 		}
2153 
2154 		struct Foo { int bar; }
2155 		assertHTTPStatus(fromRestString!(Foo)("foo"), HTTPStatus.badRequest);
2156 	}
2157 }
2158 
2159 private string generateModuleImports(I)()
2160 {
2161 	if (!__ctfe)
2162 		assert (false);
2163 
2164 	import vibe.internal.meta.codegen : getRequiredImports;
2165 	import std.algorithm : map;
2166 	import std.array : join;
2167 
2168 	auto modules = getRequiredImports!I();
2169 	return join(map!(a => "static import " ~ a ~ ";")(modules), "\n");
2170 }
2171 
2172 /***************************************************************************
2173 
2174 	The client sends the list of allowed content types in the 'Allow' http header
2175 	and the response will contain a 'Content-Type' header. This function will
2176 	try to find the best matching @SerializationResult UDA based on the allowed
2177 	content types.
2178 
2179 	Note:
2180 
2181 	Comment 1: if the request doesn't specify any allowed content types, then * / *
2182 	is assumed
2183 
2184 	Comment 2: if there are no UDA's matching the client's allowed content types, -1
2185 	is returned
2186 
2187 	Comment 3: if there are more than 1 matching UDA, for ONE specific client's allowed
2188 	content type(and their priority is the same - see below),
2189 	then the one specified earlier in the code gets chosen
2190 
2191 	Comment 4: accept-params(quality factor) and accept-extensions are ignored
2192 	https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
2193 
2194 	Comment 5: matching the most specific content type without any wildcard has priority
2195 
2196 	Comment 6: the request's content type can be in the format of
2197 
2198 	$(UL
2199 		$(LI major type / minor type)
2200 		$(LI major type / *)
2201 		$(LI * / *)
2202 	)
2203 
2204 	Params:
2205 		T = compile time known ResultSerializer classes
2206 		req_content_types_str = list of allowed content types for example:
2207 		text/*;q=0.3, text/html;q=0.7, text/html;level=1
2208 
2209 	Returns:
2210 		index of the result serializers in the T... AliasSeq if matching found,
2211 		-1 otherwise
2212 
2213 ***************************************************************************/
2214 
2215 package int get_matching_content_type (T...)(string req_content_types_str) pure @safe
2216 {
2217 	if (!req_content_types_str.strip().length)
2218 		req_content_types_str = "*/*";
2219 	struct ContentType
2220 	{
2221 		this (string major_type, string minor_type)
2222 		{
2223 			this.major_type = major_type;
2224 			this.minor_type = minor_type;
2225 			this.full_type = major_type ~ "/" ~ minor_type;
2226 			this.star_num = this.full_type.count('*'); // serves as priority
2227 		}
2228 		string major_type;
2229 		string minor_type;
2230 		string full_type;
2231 		ulong star_num;
2232 	}
2233 
2234 	// processing ResultSerializers
2235 	alias packed_UDAs = AliasSeq!(T);
2236 	ContentType[] UDA_content_types;
2237 	foreach (UDA; packed_UDAs)
2238 	{
2239 		auto ctype = UDA.contentType.splitter(';').front;
2240 		auto content_type_split = ctype.toLower().split("/");
2241 		assert(content_type_split.length == 2);
2242 		UDA_content_types ~= ContentType(content_type_split[0].strip(), content_type_split[1].strip());
2243 	}
2244 
2245 	// processing request content typess
2246 	ContentType[] req_content_types;
2247 	foreach (content_type; req_content_types_str.toLower().split(","))
2248 	{
2249 		immutable semicolon_pos = content_type.indexOf(';');
2250 		if (semicolon_pos != -1)
2251 			content_type = content_type[0 .. semicolon_pos]; // quality factor ignored
2252 		auto content_type_split = content_type.split("/");
2253 		if (content_type_split.length == 2)
2254 			req_content_types ~= ContentType(content_type_split[0].strip(), content_type_split[1].strip());
2255 	}
2256 	// sorting content types by matching preference
2257 	req_content_types.sort!(( x, y) => (x.star_num < y.star_num));
2258 
2259 	int res = -1;
2260 	ulong min_star_num = ulong.max;
2261 	foreach (UDA_ind, UDA_content_type; UDA_content_types)
2262 		foreach (const ref content_type; req_content_types)
2263 			if (
2264 					(
2265 						(
2266 							UDA_content_type.major_type == content_type.major_type &&
2267 							UDA_content_type.minor_type == content_type.minor_type
2268 						) ||
2269 						(
2270 							UDA_content_type.major_type == content_type.major_type &&
2271 							content_type.minor_type == "*"
2272 						) ||
2273 						(
2274 							content_type.major_type == "*" && content_type.minor_type == "*"
2275 						)
2276 					) &&
2277 					(
2278 						content_type.star_num < min_star_num
2279 					)
2280 				)
2281 				{
2282 					res = cast(int) UDA_ind;
2283 					min_star_num = content_type.star_num;
2284 				}
2285 	return res;
2286 }
2287 
2288 version(unittest)
2289 {
2290 	import std.range.interfaces : OutputRange;
2291 	import vibe.internal.interfaceproxy : InterfaceProxy;
2292 
2293 	void s (OutputRange!char, int){};
2294 	int d (InterfaceProxy!(InputStream)){return 1;}
2295 
2296 	int test1();
2297 
2298 	@resultSerializer!(s,d,"text/plain")
2299 	@resultSerializer!(s,d," aPPliCatIon  /  jsOn  ")
2300 	int test2();
2301 }
2302 
2303 unittest
2304 {
2305 	alias res = ResultSerializersT!(test1);
2306 	assert(res.length == 1);
2307 	assert(res[0].contentType == "application/json; charset=UTF-8");
2308 
2309 	assert(get_matching_content_type!(res)("application/json") == 0);
2310 	assert(get_matching_content_type!(res)("application/*") == 0);
2311 	assert(get_matching_content_type!(res)("  appliCAtIon /  *") == 0);
2312 	assert(get_matching_content_type!(res)("*/*") == 0);
2313 	assert(get_matching_content_type!(res)("") == 0);
2314 	assert(get_matching_content_type!(res)("application/blabla") == -1);
2315 
2316 	alias res2 = ResultSerializersT!(test2);
2317 	assert(res2.length == 2);
2318 	assert(res2[0].contentType == "text/plain");
2319 	assert(res2[1].contentType == " aPPliCatIon  /  jsOn  ");
2320 
2321 	assert(get_matching_content_type!(res2)("text/plain, application/json") == 0);
2322 	assert(get_matching_content_type!(res2)("text/*, application/json") == 1);
2323 	assert(get_matching_content_type!(res2)("*/*, application/json") == 1);
2324 	assert(get_matching_content_type!(res2)("*/*") == 0);
2325 	assert(get_matching_content_type!(res2)("") == 0);
2326 	assert(get_matching_content_type!(res2)("blabla/blabla, blublu/blublu") == -1);
2327 }
2328 
2329 version(unittest)
2330 {
2331 	private struct Aggregate { }
2332 	private interface Interface
2333 	{
2334 		Aggregate[] foo();
2335 	}
2336 }
2337 
2338 unittest
2339 {
2340 	enum imports = generateModuleImports!Interface;
2341 	static assert (imports == "static import vibe.web.rest;");
2342 }
2343 
2344 // Check that the interface is valid. Every checks on the correctness of the
2345 // interface should be put in checkRestInterface, which allows to have consistent
2346 // errors in the server and client.
2347 package string getInterfaceValidationError(I)()
2348 out (result) { assert((result is null) == !result.length); }
2349 do {
2350 	import vibe.web.internal.rest.common : ParameterKind, WebParamUDATuple;
2351 	import std.typetuple : TypeTuple;
2352 	import std.algorithm : strip;
2353 
2354 	// The hack parameter is to kill "Statement is not reachable" warnings.
2355 	string validateMethod(alias Func)(bool hack = true) {
2356 		import vibe.internal.meta.uda;
2357 		import std.string : format;
2358 
2359 		static assert(is(FunctionTypeOf!Func), "Internal error");
2360 
2361 		if (!__ctfe)
2362 			assert(false, "Internal error");
2363 
2364 		enum FuncId = (fullyQualifiedName!I~ "." ~ __traits(identifier, Func));
2365 		alias PT = ParameterTypeTuple!Func;
2366 		static if (!__traits(compiles, ParameterIdentifierTuple!Func)) {
2367 			if (hack) return "%s: A parameter has no name.".format(FuncId);
2368 			alias PN = TypeTuple!("-DummyInvalid-");
2369 		} else
2370 			alias PN = ParameterIdentifierTuple!Func;
2371 		alias WPAT = UDATuple!(WebParamAttribute, Func);
2372 
2373 		// Check if there is no orphan UDATuple (e.g. typo while writing the name of the parameter).
2374 		foreach (i, uda; WPAT) {
2375 			// Note: static foreach gets unrolled, generating multiple nested sub-scope.
2376 			// The spec / DMD doesn't like when you have the same symbol in those,
2377 			// leading to wrong codegen / wrong template being reused.
2378 			// That's why those templates need different names.
2379 			// See DMD bug #9748.
2380 			mixin(GenOrphan!(i).Decl);
2381 			// template CmpOrphan(string name) { enum CmpOrphan = (uda.identifier == name); }
2382 			static if (!anySatisfy!(mixin(GenOrphan!(i).Name), PN)) {
2383 				if (hack) return "%s: No parameter '%s' (referenced by attribute @%sParam)"
2384 					.format(FuncId, uda.identifier, uda.origin);
2385 			}
2386 		}
2387 
2388 		foreach (i, P; PT) {
2389 			static if (!PN[i].length)
2390 				if (hack) return "%s: Parameter %d has no name."
2391 					.format(FuncId, i);
2392 			// Check for multiple origins
2393 			static if (WPAT.length) {
2394 				// It's okay to reuse GenCmp, as the order of params won't change.
2395 				// It should/might not be reinstantiated by the compiler.
2396 				mixin(GenCmp!("Loop", i, PN[i]).Decl);
2397 				alias WPA = Filter!(mixin(GenCmp!("Loop", i, PN[i]).Name), WPAT);
2398 				static if (WPA.length > 1)
2399 					if (hack) return "%s: Parameter '%s' has multiple @*Param attributes on it."
2400 						.format(FuncId, PN[i]);
2401 			}
2402 		}
2403 
2404 		// Check for misplaced out and non-const ref
2405 		alias PSC = ParameterStorageClass;
2406 		foreach (i, SC; ParameterStorageClassTuple!Func) {
2407 			static if (SC & PSC.out_ || (SC & PSC.ref_ && !is(ConstOf!(PT[i]) == PT[i])) ) {
2408 				mixin(GenCmp!("Loop", i, PN[i]).Decl);
2409 				alias Attr = TypeTuple!(
2410 					WebParamUDATuple!(Func, i),
2411 					Filter!(mixin(GenCmp!("Loop", i, PN[i]).Name), WPAT),
2412 				);
2413 				static if (Attr.length != 1) {
2414 					if (hack) return "%s: Parameter '%s' cannot be %s"
2415 						.format(FuncId, PN[i], SC & PSC.out_ ? "out" : "ref");
2416 				} else static if (Attr[0].origin != ParameterKind.header) {
2417 					if (hack) return "%s: %s parameter '%s' cannot be %s"
2418 						.format(FuncId, Attr[0].origin, PN[i],
2419 							SC & PSC.out_ ? "out" : "ref");
2420 				}
2421 			}
2422 		}
2423 
2424 		// Check for @path(":name")
2425 		enum pathAttr = findFirstUDA!(PathAttribute, Func);
2426 		static if (pathAttr.found) {
2427 			static if (!pathAttr.value.length) {
2428 				if (hack)
2429 					return "%s: Path is null or empty".format(FuncId);
2430 			} else {
2431 				import std.algorithm : canFind, splitter;
2432 				// splitter doesn't work with alias this ?
2433 				auto str = pathAttr.value.data;
2434 				if (str.canFind("//")) return "%s: Path '%s' contains empty entries.".format(FuncId, pathAttr.value);
2435 				str = str.strip('/');
2436 				if (!str.length) return null;
2437 				foreach (elem; str.splitter('/')) {
2438 					assert(elem.length, "Empty path entry not caught yet!?");
2439 
2440 					if (elem[0] == ':') {
2441 						// typeof(PN) is void when length is 0.
2442 						static if (!PN.length) {
2443 							if (hack)
2444 								return "%s: Path contains '%s', but no parameter '_%s' defined."
2445 									.format(FuncId, elem, elem[1..$]);
2446 						} else {
2447 							if (![PN].canFind("_"~elem[1..$]))
2448 								if (hack) return "%s: Path contains '%s', but no parameter '_%s' defined."
2449 									.format(FuncId, elem, elem[1..$]);
2450 							elem = elem[1..$];
2451 						}
2452 					}
2453 				}
2454 				// TODO: Check for validity of the subpath.
2455 			}
2456 		}
2457 		return null;
2458 	}
2459 
2460 	if (!__ctfe)
2461 		assert(false, "Internal error");
2462 	bool hack = true;
2463 	foreach (method; __traits(allMembers, I)) {
2464 		// WORKAROUND #1045 / @@BUG14375@@
2465 		static if (method.length != 0)
2466 			foreach (overload; MemberFunctionsTuple!(I, method)) {
2467 				static if (validateMethod!(overload)())
2468 					if (hack) return validateMethod!(overload)();
2469 			}
2470 	}
2471 	return null;
2472 }
2473 
2474 // Test detection of user typos (e.g., if the attribute is on a parameter that doesn't exist).
2475 unittest {
2476 	enum msg = "No parameter 'ath' (referenced by attribute @headerParam)";
2477 
2478 	interface ITypo {
2479 		@headerParam("ath", "Authorization") // mistyped parameter name
2480 		string getResponse(string auth);
2481 	}
2482 	enum err = getInterfaceValidationError!ITypo;
2483 	static assert(err !is null && stripTestIdent(err) == msg,
2484 		"Expected validation error for getResponse, got: "~stripTestIdent(err));
2485 }
2486 
2487 // Multiple origin for a parameter
2488 unittest {
2489 	enum msg = "Parameter 'arg1' has multiple @*Param attributes on it.";
2490 
2491 	interface IMultipleOrigin {
2492 		@headerParam("arg1", "Authorization") @bodyParam("arg1", "Authorization")
2493 		string getResponse(string arg1, int arg2);
2494 	}
2495 	enum err = getInterfaceValidationError!IMultipleOrigin;
2496 	static assert(err !is null && stripTestIdent(err) == msg, err);
2497 }
2498 
2499 // Missing parameter name
2500 unittest {
2501 	enum msg = "Parameter 0 has no name.";
2502 
2503 	interface IMissingName1 {
2504 		string getResponse(string = "troublemaker");
2505 	}
2506 	interface IMissingName2 {
2507 		string getResponse(string);
2508 	}
2509 	enum err1 = getInterfaceValidationError!IMissingName1;
2510 	static assert(err1 !is null && stripTestIdent(err1) == msg, err1);
2511 	enum err2 = getInterfaceValidationError!IMissingName2;
2512 	static assert(err2 !is null && stripTestIdent(err2) == msg, err2);
2513 }
2514 
2515 // Issue 949
2516 unittest {
2517 	enum msg = "Path contains ':owner', but no parameter '_owner' defined.";
2518 
2519 	@path("/repos/")
2520 	interface IGithubPR {
2521 		@path(":owner/:repo/pulls")
2522 		string getPullRequests(string owner, string repo);
2523 	}
2524 	enum err = getInterfaceValidationError!IGithubPR;
2525 	static assert(err !is null && stripTestIdent(err) == msg, err);
2526 }
2527 
2528 // Issue 1017
2529 unittest {
2530 	interface TestSuccess { @path("/") void test(); }
2531 	interface TestSuccess2 { @path("/test/") void test(); }
2532 	interface TestFail { @path("//") void test(); }
2533 	interface TestFail2 { @path("/test//it/") void test(); }
2534 	static assert(getInterfaceValidationError!TestSuccess is null);
2535 	static assert(getInterfaceValidationError!TestSuccess2 is null);
2536 	static assert(stripTestIdent(getInterfaceValidationError!TestFail)
2537 		== "Path '//' contains empty entries.");
2538 	static assert(stripTestIdent(getInterfaceValidationError!TestFail2)
2539 		== "Path '/test//it/' contains empty entries.");
2540 }
2541 
2542 unittest {
2543 	interface NullPath  { @path(null) void test(); }
2544 	interface ExplicitlyEmptyPath { @path("") void test(); }
2545 	static assert(stripTestIdent(getInterfaceValidationError!NullPath)
2546 				  == "Path is null or empty");
2547 	static assert(stripTestIdent(getInterfaceValidationError!ExplicitlyEmptyPath)
2548 				  == "Path is null or empty");
2549 
2550 	// Note: Implicitly empty path are valid:
2551 	// interface ImplicitlyEmptyPath { void get(); }
2552 }
2553 
2554 // Accept @headerParam ref / out parameters
2555 unittest {
2556 	interface HeaderRef {
2557 		@headerParam("auth", "auth")
2558 		string getData(ref string auth);
2559 		string getData2(@viaHeader("auth") ref string auth);
2560 	}
2561 	static assert(getInterfaceValidationError!HeaderRef is null,
2562 		      stripTestIdent(getInterfaceValidationError!HeaderRef));
2563 
2564 	interface HeaderOut {
2565 		@headerParam("auth", "auth")
2566 		void getData(out string auth);
2567 		void getData(@viaHeader("auth") out string auth);
2568 	}
2569 	static assert(getInterfaceValidationError!HeaderOut is null,
2570 		      stripTestIdent(getInterfaceValidationError!HeaderOut));
2571 }
2572 
2573 // Reject unattributed / @queryParam or @bodyParam ref / out parameters
2574 unittest {
2575 	interface QueryRef {
2576 		@queryParam("auth", "auth")
2577 		string getData(ref string auth);
2578 	}
2579 	static assert(stripTestIdent(getInterfaceValidationError!QueryRef)
2580 		== "query parameter 'auth' cannot be ref");
2581 
2582 	interface QueryRefConst {
2583 		@queryParam("auth", "auth")
2584 		string getData(const ref string auth);
2585 	}
2586 	enum err1 = getInterfaceValidationError!QueryRefConst;
2587 	static assert(err1 is null, err1);
2588 
2589 	interface QueryOut {
2590 		@queryParam("auth", "auth")
2591 		void getData(out string auth);
2592 	}
2593 	static assert(stripTestIdent(getInterfaceValidationError!QueryOut)
2594 		== "query parameter 'auth' cannot be out");
2595 
2596 	interface BodyRef {
2597 		@bodyParam("auth", "auth")
2598 		string getData(ref string auth);
2599 	}
2600 	static assert(stripTestIdent(getInterfaceValidationError!BodyRef)
2601 		== "body_ parameter 'auth' cannot be ref",x);
2602 
2603 	interface BodyRefConst {
2604 		@bodyParam("auth", "auth")
2605 		string getData(const ref string auth);
2606 	}
2607 	enum err2 = getInterfaceValidationError!BodyRefConst;
2608 	static assert(err2 is null, err2);
2609 
2610 	interface BodyOut {
2611 		@bodyParam("auth", "auth")
2612 		void getData(out string auth);
2613 	}
2614 	static assert(stripTestIdent(getInterfaceValidationError!BodyOut)
2615 		== "body_ parameter 'auth' cannot be out");
2616 
2617 	// There's also the possibility of someone using an out unnamed
2618 	// parameter (don't ask me why), but this is catched as unnamed
2619 	// parameter, so we don't need to check it here.
2620 }
2621 
2622 private string stripTestIdent(string msg)
2623 @safe {
2624 	import std.string;
2625 	auto idx = msg.indexOf(": ");
2626 	return idx >= 0 ? msg[idx+2 .. $] : msg;
2627 }
2628 
2629 // Small helper for client code generation
2630 private string paramCTMap(string[string] params)
2631 @safe {
2632 	import std.array : appender, join;
2633 	if (!__ctfe)
2634 		assert (false, "This helper is only supposed to be called for codegen in RestClientInterface.");
2635 	auto app = appender!(string[]);
2636 	foreach (key, val; params) {
2637 		app ~= "\""~key~"\"";
2638 		app ~= val;
2639 	}
2640 	return app.data.join(", ");
2641 }
2642 
2643 package string stripTUnderscore(string name, RestInterfaceSettings settings)
2644 @safe {
2645 	if ((settings is null || settings.stripTrailingUnderscore)
2646 	    && name.endsWith("_"))
2647 		return name[0 .. $-1];
2648 	else return name;
2649 }
2650 
2651 // Workarounds @@DMD:9748@@, and maybe more
2652 package template GenCmp(string name, int id, string cmpTo) {
2653 	import std.string : format;
2654 	import std.conv : to;
2655 	enum Decl = q{
2656 		template %1$s(alias uda) {
2657 			enum %1$s = (uda.identifier == "%2$s");
2658 		}
2659 	}.format(Name, cmpTo);
2660 	enum Name = name~to!string(id);
2661 }
2662 
2663 // Ditto
2664 private template GenOrphan(int id) {
2665 	import std.string : format;
2666 	import std.conv : to;
2667 	enum Decl = q{
2668 		template %1$s(string name) {
2669 			enum %1$s = (uda.identifier == name);
2670 		}
2671 	}.format(Name);
2672 	enum Name = "OrphanCheck"~to!string(id);
2673 }
2674 
2675 // Workaround for issue #1045 / DMD bug 14375
2676 // Also, an example of policy-based design using this module.
2677 @safe unittest {
2678 	import std.traits, std.typetuple;
2679 	import vibe.internal.meta.codegen;
2680 	import vibe.internal.meta.typetuple;
2681 	import vibe.web.internal.rest.common : ParameterKind;
2682 
2683 	interface Policies {
2684 		@headerParam("auth", "Authorization")
2685 		string BasicAuth(string auth, ulong expiry) @safe;
2686 	}
2687 
2688 	@path("/keys/")
2689 	interface IKeys(alias AuthenticationPolicy = Policies.BasicAuth) {
2690 		static assert(is(FunctionTypeOf!AuthenticationPolicy == function),
2691 			      "Policies needs to be functions");
2692 		@path("/") @method(HTTPMethod.POST) @safe
2693 		mixin CloneFunctionDecl!(AuthenticationPolicy, true, "create");
2694 	}
2695 
2696 	class KeysImpl : IKeys!() {
2697 	override:
2698 		string create(string auth, ulong expiry) @safe {
2699 			return "4242-4242";
2700 		}
2701 	}
2702 
2703 	// Some sanity checks
2704         // Note: order is most likely implementation dependent.
2705 	// Good thing we only have one frontend...
2706 	alias WPA = WebParamAttribute;
2707 	static assert(Compare!(
2708 			      Group!(__traits(getAttributes, IKeys!().create)),
2709 			      Group!(PathAttribute("/"),
2710 				     MethodAttribute(HTTPMethod.POST),
2711 				     WPA(ParameterKind.header, "auth", "Authorization"))));
2712 
2713 	void register() @safe {
2714 		auto router = new URLRouter();
2715 		router.registerRestInterface(new KeysImpl());
2716 	}
2717 
2718 	void query() @safe {
2719 		auto client = new RestInterfaceClient!(IKeys!())("http://127.0.0.1:8080");
2720 		assert(client.create("Hello", 0) == "4242-4242");
2721 	}
2722 }
2723 
2724 @safe unittest { // @noRoute support in RestInterfaceClient
2725 	interface I {
2726 		void foo();
2727 		@noRoute int bar(void* someparam);
2728 	}
2729 	auto cli = new RestInterfaceClient!I("http://127.0.0.1/");
2730 }