1 /** 2 * Модуль реализации сервер web application 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.server; 11 12 private 13 { 14 import vibe.core.core; 15 import vibe.http.server; 16 import vibe.stream.tls : createTLSContext, TLSContext, TLSContextKind; 17 18 import uniconf.core : UniConf; 19 20 import dango.system.logging; 21 import dango.system.properties; 22 } 23 24 25 /** 26 * Интерфейс Web сервера 27 */ 28 interface WebApplicationServer 29 { 30 /** 31 * Запуск сервера 32 */ 33 void listen(); 34 35 36 /** 37 * Остановка сервера 38 */ 39 void shutdown(); 40 } 41 42 43 /** 44 * Класс веб сервера 45 */ 46 class HTTPApplicationServer : WebApplicationServer 47 { 48 private 49 { 50 HTTPListener[] _listeners; 51 HTTPServerSettings _httpSettings; 52 HTTPServerRequestHandler _handler; 53 } 54 55 56 this(HTTPServerSettings settings, HTTPServerRequestHandler handler) @safe 57 { 58 this._httpSettings = settings; 59 this._handler = handler; 60 } 61 62 /** 63 * Запуск сервера 64 */ 65 void listen() 66 { 67 runWorkerTaskDist!(runWorker)(cast(shared)this); 68 logInfo("HTTP Application Server start"); 69 } 70 71 /** 72 * Остановка сервера 73 */ 74 void shutdown() 75 { 76 foreach (HTTPListener listener; _listeners) 77 listener.stopListening(); 78 logInfo("HTTP Application Server stop"); 79 } 80 81 82 private void runWorker() shared 83 { 84 HTTPServerSettings settings = cast(HTTPServerSettings)_httpSettings; 85 settings.options = HTTPServerOption.reusePort; 86 HTTPServerRequestHandler handler = cast(HTTPServerRequestHandler)_handler; 87 _listeners ~= cast(shared)listenHTTP(settings, handler); 88 } 89 } 90 91 92 /** 93 * Функция стоит объект настроект http сервера по параметрам конфигурации 94 * Params: 95 * 96 * config = Конфигурация 97 */ 98 HTTPServerSettings loadHTTPServerSettings(UniConf config) @safe 99 { 100 import core.time : dur; 101 import std.algorithm.iteration : map; 102 import std.array : array; 103 104 HTTPServerSettings settings = new HTTPServerSettings(); 105 106 auto host = config.getOrEnforce!UniConf("host", "Not defined host property"); 107 settings.bindAddresses = host.toSequence().map!((h) { 108 return h.get!string; 109 }).array; 110 111 settings.port = config.getOrEnforce!ushort("port", "Not defined port property"); 112 settings.options = HTTPServerOption.defaults; 113 114 if ("hostName" in config) 115 settings.hostName = config.get!string("hostName"); 116 117 if ("maxRequestTime" in config) 118 settings.maxRequestTime = dur!"seconds"(config.get!long("maxRequestTime")); 119 120 if ("keepAliveTimeout" in config) 121 settings.keepAliveTimeout = dur!"seconds"(config.get!long("keepAliveTimeout")); 122 123 if ("maxRequestSize" in config) 124 settings.maxRequestSize = config.get!long("maxRequestSize"); 125 126 if ("maxRequestHeaderSize" in config) 127 settings.maxRequestHeaderSize = config.get!long("maxRequestHeaderSize"); 128 129 if ("accessLogFormat" in config) 130 settings.accessLogFormat = config.get!string("accessLogFormat"); 131 132 if ("accessLogFile" in config) 133 settings.accessLogFile = config.get!string("accessLogFile"); 134 135 settings.accessLogToConsole = config.getOrElse("accessLogToConsole", false); 136 137 if ("ssl" in config) 138 { 139 UniConf sslConfig = config.get!UniConf("ssl"); 140 settings.tlsContext = createTLSContextFrom(sslConfig); 141 } 142 143 return settings; 144 } 145 146 147 /** 148 * Создание TLS контекста из конфигурации сервиса 149 */ 150 TLSContext createTLSContextFrom(UniConf sslConfig) @safe 151 { 152 TLSContext tlsCtx = createTLSContext(TLSContextKind.server); 153 154 auto certChainFile = sslConfig.getOrEnforce!string("certificateChainFile", 155 "Not defined certificateChainFile property"); 156 auto privateKeyFile = sslConfig.getOrEnforce!string("privateKeyFile", 157 "Not defined privateKeyFile property"); 158 159 tlsCtx.useCertificateChainFile(certChainFile); 160 tlsCtx.usePrivateKeyFile(privateKeyFile); 161 162 return tlsCtx; 163 } 164 165 166 /** 167 * Добаляет перфикс к url пути 168 */ 169 string joinInetPath(string prefix, string path) @safe 170 { 171 import vibe.core.path : InetPath; 172 173 auto parent = InetPath(prefix); 174 auto child = InetPath(path); 175 176 if (!parent.absolute) 177 parent = InetPath("/") ~ parent; 178 179 if (child.absolute) 180 { 181 auto childSegments = child.bySegment(); 182 childSegments.popFront(); 183 child = InetPath(childSegments); 184 } 185 186 if (!child.empty) 187 parent ~= child; 188 189 return parent.toString; 190 } 191 192 @("Should work joinInetPath method") 193 @safe unittest 194 { 195 assert (joinInetPath("/", "api") == "/api"); 196 assert (joinInetPath("", "api") == "/api"); 197 assert (joinInetPath("/", "/api") == "/api"); 198 assert (joinInetPath("", "/api") == "/api"); 199 } 200