1 /** 2 * Реализация плагина для содания веб приложения 3 * 4 * Copyright: (c) 2015-2020, Milofon Project. 5 * License: Subject to the terms of the BSD 3-Clause License, as written in the included LICENSE.md file. 6 * Author: <m.galanin@milofon.pro> Maksim Galanin 7 * Date: 2020-04-29 8 */ 9 10 module dango.web.plugin; 11 12 public 13 { 14 import dango.web.server : WebApplicationServer; 15 } 16 17 private 18 { 19 import std.algorithm.searching : canFind; 20 import std.algorithm.iteration : filter, map; 21 import std.algorithm.sorting : sort; 22 import std.format : fmt = format; 23 import std.array : array; 24 import std.uni : toUpper; 25 26 import vibe.http.router : URLRouter; 27 28 import dango.inject; 29 import dango.system.application : Application, UniConf; 30 import dango.system.properties; 31 import dango.system.exception; 32 import dango.system.logging; 33 import dango.system.plugin; 34 35 import dango.web.middleware; 36 import dango.web.controller; 37 import dango.web.server; 38 } 39 40 41 /** 42 * Плагин поддерживающий обработку запросов 43 */ 44 interface WebPlugin : Plugin 45 { 46 /** 47 * Регистрация сервера 48 */ 49 void registerServer(void delegate(WebApplicationServer) dg) @safe; 50 } 51 52 53 /** 54 * Реализация демона для запуска web приложения 55 */ 56 class WebApplicationPlugin : DaemonPlugin, PluginContainer!WebPlugin 57 { 58 private 59 { 60 WebApplicationServer[] _servers; 61 WebPlugin[] _plugins; 62 } 63 64 65 /** 66 * Свойство возвращает наименование плагина 67 */ 68 string name() pure @safe nothrow 69 { 70 return "WEB"; 71 } 72 73 /** 74 * Свойство возвращает версию приложения 75 */ 76 SemVer release() pure nothrow @safe 77 { 78 return SemVer(0, 0, 1); 79 } 80 81 /** 82 * Запуск процесса 83 */ 84 int startDaemon() 85 { 86 foreach (plugin; _plugins) 87 plugin.registerServer((server) { 88 _servers ~= server; 89 }); 90 91 foreach (WebApplicationServer server; _servers) 92 server.listen(); 93 94 return 0; 95 } 96 97 /** 98 * Остановка процесса 99 * 100 * Params: 101 * exitStatus = Код завершения приложения 102 */ 103 int stopDaemon(int exitStatus) 104 { 105 foreach (WebApplicationServer server; _servers) 106 server.shutdown(); 107 return exitStatus; 108 } 109 110 /** 111 * Регистрация плагина 112 * Params: 113 * plugin = Плагин для регистрации 114 */ 115 void collectPlugin(WebPlugin plugin) @safe nothrow 116 { 117 _plugins ~= plugin; 118 } 119 } 120 121 122 /** 123 * Контекст регистрации компонентов контроллера 124 */ 125 alias WebServerContext = PluginContext!(WebServerPlugin); 126 127 128 /** 129 * Плагин сервера с роутингом 130 */ 131 class WebServerPlugin : WebPlugin 132 { 133 private 134 { 135 WebControllerFactory[string] _controllers; 136 WebMiddlewareFactory[string] _middlewares; 137 DependencyContainer _container; 138 UniConf _config; 139 } 140 141 142 /** 143 * Main constructor 144 */ 145 @Inject 146 this(Application application) 147 { 148 this._container = application.getContainer(); 149 this._config = application.getConfig(); 150 } 151 152 /** 153 * Свойство возвращает наименование плагина 154 */ 155 string name() pure @safe nothrow 156 { 157 return "Web Server"; 158 } 159 160 /** 161 * Свойство возвращает версию приложения 162 */ 163 SemVer release() pure nothrow @safe 164 { 165 return SemVer(0, 0, 1); 166 } 167 168 /** 169 * Регистрация сервера 170 */ 171 void registerServer(void delegate(WebApplicationServer) @safe dg) @safe 172 { 173 auto webConfigs = _config.getOrEnforce!UniConf("web", 174 "Not found web application configurations"); 175 176 foreach (UniConf webConf; webConfigs.toSequence()) 177 { 178 if (webConf.getOrElse("enabled", false)) 179 { 180 if (auto server = createServer(webConf)) 181 dg(server); 182 } 183 } 184 } 185 186 /** 187 * Регистрация middleware 188 */ 189 void registerMiddleware(M : WebMiddleware)(string name) @safe 190 { 191 alias MF = ComponentFactoryCtor!(WebMiddleware, M, UniConf); 192 registerMiddleware!(MF)(name); 193 } 194 195 /** 196 * Регистрация middleware с использованием фабрики 197 */ 198 void registerMiddleware(MF : WebMiddlewareFactory)(string name) @safe 199 { 200 auto factory = new WrapDependencyFactory!(MF)(); 201 registerMiddleware!MF(name, factory); 202 } 203 204 /** 205 * Регистрация middleware с использованием существующей фабрики 206 */ 207 void registerMiddleware(MF : WebMiddlewareFactory)(string name, 208 WebMiddlewareFactory factory) @safe 209 { 210 string uName = name.toUpper; 211 _middlewares[uName] = factory; 212 } 213 214 /** 215 * Регистрация контроллера 216 */ 217 void registerController(C : WebController)(string name) @safe 218 { 219 alias CF = ComponentFactoryCtor!(WebController, C, UniConf); 220 registerController!(CF)(name); 221 } 222 223 /** 224 * Регистрация контроллера с использованием фабрики 225 */ 226 void registerController(CF : WebControllerFactory)(string name) @safe 227 { 228 auto factory = new WrapDependencyFactory!(CF)(); 229 registerController!CF(name, factory); 230 } 231 232 /** 233 * Регистрация контроллера с использованием существующей фабрики 234 */ 235 void registerController(CF : WebControllerFactory)(string name, 236 WebControllerFactory factory) @safe 237 { 238 string uName = name.toUpper; 239 _controllers[uName] = factory; 240 } 241 242 243 private: 244 245 246 WebApplicationServer createServer(UniConf config) @safe 247 { 248 string webName = config.getOrElse!string("__name", "Undefined"); 249 logInfo("Configuring web server '%s'", webName); 250 251 URLRouter routes = new URLRouter(); 252 auto settings = loadHTTPServerSettings(config); 253 254 MiddlewareInfo[] middlewares; 255 256 foreach (UniConf mdwConf; config.toSequence("middleware")) 257 { 258 string mdwName = mdwConf.getNameOrEnforce( 259 "Not defined middleware name"); 260 261 long ordering = mdwConf.getOrElse!long("order", 0); 262 string label = mdwConf.getOrElse!string("label", mdwName); 263 enforceConfig(!middlewares.canFind!((m) { 264 return m.label == label; 265 }), fmt!"Middleware '%s' already defined"(label)); 266 267 auto mdwFactory = mdwName.toUpper in _middlewares; 268 enforceConfig(mdwFactory !is null, 269 fmt!"Middleware '%s' not register"(mdwName)); 270 271 middlewares ~= MiddlewareInfo(label, ordering, mdwConf, *mdwFactory); 272 } 273 274 foreach (UniConf ctrConf; config.toSequence("controller") 275 .filter!((c) => c.getOrElse("enabled", false))) 276 { 277 string ctrName = getNameOrEnforce(ctrConf, 278 "Not defined controller name"); 279 280 auto ctrlFactory = ctrName.toUpper in _controllers; 281 enforceConfig(ctrlFactory !is null, 282 fmt!"Controller '%s' not register"(ctrName)); 283 284 auto ctrl = ctrlFactory.createComponent(_container, ctrConf); 285 286 auto ctrlMiddlewares = ctrConf.toSequence("middlewares") 287 .map!(pm => pm.get!string); 288 289 // проверка на наличие конфигураций 290 foreach (string mdwLabel; ctrlMiddlewares) 291 enforceConfig(middlewares.canFind!((m) => m.label == mdwLabel), 292 fmt!"Middleware %s not found configuration"(mdwLabel)); 293 294 auto activeMiddlewares = middlewares.filter!((mdwConf) { 295 bool def = mdwConf.config.getOrElse!bool("default", false); 296 return def || ctrlMiddlewares.canFind(mdwConf.label); 297 }).array; 298 299 activeMiddlewares.sort!((a, b) => a.ordering > b.ordering); 300 301 logInfo("Register controller: '%s'", ctrName); 302 logInfo(" Activated middlewares: %s", activeMiddlewares 303 .map!(m => m.label)); 304 305 string prefix = ctrConf.getOrElse("prefix", ""); 306 307 ctrl.registerChains((HTTPMethod method, string path, Chain ch) @safe { 308 string absPath = joinInetPath(prefix, path); 309 foreach (mdwInfo; activeMiddlewares) 310 { 311 WebMiddleware mdw = mdwInfo.factory.createComponent( 312 _container, mdwInfo.config); 313 ch.attachMiddleware(mdw); 314 mdw.registerHandlers(method, absPath, (mm, mp, mh) @safe { 315 routes.match(mm, mp, mh); 316 }); 317 } 318 logInfo(" %s: %s", method, absPath); 319 routes.match(method, absPath, ch); 320 }); 321 } 322 323 routes.rebuild(); 324 325 return new HTTPApplicationServer(settings, routes); 326 } 327 328 329 struct MiddlewareInfo 330 { 331 string label; 332 long ordering; 333 UniConf config; 334 WebMiddlewareFactory factory; 335 } 336 } 337