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