1 /* 2 *Copyright (C) 2018 Laurent Tréguier 3 * 4 *This file is part of DLS. 5 * 6 *DLS is free software: you can redistribute it and/or modify 7 *it under the terms of the GNU General Public License as published by 8 *the Free Software Foundation, either version 3 of the License, or 9 *(at your option) any later version. 10 * 11 *DLS is distributed in the hope that it will be useful, 12 *but WITHOUT ANY WARRANTY; without even the implied warranty of 13 *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 *GNU General Public License for more details. 15 * 16 *You should have received a copy of the GNU General Public License 17 *along with DLS. If not, see <http://www.gnu.org/licenses/>. 18 * 19 */ 20 21 module dls.server; 22 23 private immutable contentLengthHeaderName = "Content-Length"; 24 25 shared static this() 26 { 27 import dls.protocol.handlers : pushHandler; 28 import dls.util.setup : initialSetup; 29 import std.algorithm : map; 30 import std.array : join, split; 31 import std.meta : Alias, AliasSeq; 32 import std.traits : hasUDA, isSomeFunction, select; 33 import std.typecons : tuple; 34 import std..string : capitalize; 35 36 initialSetup(); 37 38 foreach (modName; AliasSeq!("general", "client", "text_document", "window", 39 "workspace", "other")) 40 { 41 mixin("import dls.protocol.messages." ~ modName ~ ";"); 42 mixin("alias mod = dls.protocol.messages." ~ modName ~ ";"); 43 44 foreach (thing; __traits(allMembers, mod)) 45 { 46 alias t = Alias!(__traits(getMember, mod, thing)); 47 48 static if (__traits(getProtection, t) == "public" && isSomeFunction!t) 49 { 50 enum attrs = tuple(__traits(getAttributes, t)); 51 enum attrsWithDefaults = tuple(modName[0] ~ modName.split('_') 52 .map!capitalize().join()[1 .. $], thing, attrs.expand); 53 enum parts = tuple(attrsWithDefaults[attrs.length > 0 ? 2 : 0], 54 attrsWithDefaults[attrs.length > 1 ? 3 : 1]); 55 enum method = select!(parts[0].length != 0)(parts[0] ~ "/", "") ~ parts[1]; 56 57 pushHandler(method, &t); 58 } 59 } 60 } 61 } 62 63 final abstract class Server 64 { 65 import dls.protocol.interfaces : InitializeParams; 66 import dls.util.disposable_fiber : DisposableFiber; 67 import std.container : DList; 68 import std.json : JSONValue; 69 70 static bool exit; 71 private static bool _initialized; 72 private static DisposableFiber[string] _requestsFibers; 73 private static DList!DisposableFiber _fibers; 74 75 @property static bool initialized() 76 { 77 return _initialized; 78 } 79 80 @property static void initialized(bool i) 81 { 82 import dls.protocol.logger : logger; 83 84 _initialized = i; 85 86 if (!_initialized) 87 { 88 logger.info("Cancelling pending requests"); 89 90 foreach (fiber; _requestsFibers.byValue) 91 { 92 fiber.dispose(); 93 } 94 } 95 } 96 97 static void cancel(JSONValue id) 98 { 99 import dls.protocol.logger : logger; 100 101 immutable idString = id.toString(); 102 103 if (idString in _requestsFibers 104 && _requestsFibers[idString].state != DisposableFiber.State.TERM) 105 { 106 logger.info("Cancelling request %s", idString); 107 _requestsFibers[idString].dispose(); 108 } 109 } 110 111 static void loop() 112 { 113 import dls.protocol.logger : logger; 114 import dls.protocol.messages.general : shutdown; 115 import dls.util.communicator : communicator; 116 import std.algorithm : findSplit; 117 import std.array : appender; 118 import std.conv : to; 119 import std..string : strip, stripRight; 120 121 auto lineAppender = appender!(char[]); 122 string[string] headers; 123 string line; 124 125 while (!exit && communicator.hasData()) 126 { 127 headers.clear(); 128 129 do 130 { 131 bool cr; 132 bool lf; 133 134 lineAppender.clear(); 135 136 do 137 { 138 auto res = communicator.read(1); 139 140 if (res.length == 0) 141 { 142 break; 143 } 144 145 lineAppender ~= res[0]; 146 147 if (cr) 148 { 149 lf = res[0] == '\n'; 150 } 151 152 cr = res[0] == '\r'; 153 } 154 while (!lf); 155 156 line = lineAppender.data.stripRight().idup; 157 auto parts = line.findSplit(":"); 158 159 if (parts[1].length > 0) 160 { 161 headers[parts[0]] = parts[2]; 162 } 163 } 164 while (line.length > 0); 165 166 if (headers.length == 0) 167 { 168 continue; 169 } 170 171 if (contentLengthHeaderName !in headers) 172 { 173 logger.error("No valid %s section in header", contentLengthHeaderName); 174 continue; 175 } 176 177 const content = communicator.read(headers[contentLengthHeaderName].strip().to!size_t); 178 auto fiber = new DisposableFiber({ handleJSON(content); }); 179 180 fiber.call(); 181 _fibers.insertBack(fiber); 182 183 while (!_initialized || !communicator.hasPendingData()) 184 { 185 while (!_fibers.empty && _fibers.front.state == DisposableFiber.State.TERM) 186 { 187 foreach (id, f; _requestsFibers) 188 { 189 if (f == _fibers.front) 190 { 191 _requestsFibers.remove(id); 192 break; 193 } 194 } 195 196 _fibers.removeFront(); 197 } 198 199 if (_fibers.empty) 200 { 201 break; 202 } 203 else 204 { 205 _fibers.front.call(); 206 } 207 } 208 } 209 210 if (_initialized) 211 { 212 logger.warning("No shutdown request or exit notification received"); 213 shutdown(JSONValue()); 214 _initialized = true; 215 } 216 } 217 218 private static void handleJSON(const char[] content) 219 { 220 import dls.protocol.errors : InvalidParamsException; 221 import dls.protocol.handlers : HandlerNotFoundException, 222 NotificationHandler, RequestHandler, ResponseHandler, handler; 223 import dls.protocol.jsonrpc : ErrorCodes, NotificationMessage, 224 RequestMessage, ResponseMessage, send, sendError; 225 import dls.protocol.logger : logger; 226 import dls.protocol.state : initOptions; 227 import dls.util.disposable_fiber : FiberDisposedException; 228 import dls.util.json : convertFromJSON; 229 import std.algorithm : startsWith; 230 import std.json : JSONException, parseJSON; 231 232 RequestMessage request; 233 NotificationMessage notification; 234 235 void findAndExecuteHandler() 236 { 237 try 238 { 239 immutable json = parseJSON(content); 240 241 if ("method" in json) 242 { 243 if ("id" in json) 244 { 245 request = convertFromJSON!RequestMessage(json); 246 _requestsFibers[request.id.toString()] = DisposableFiber.getThis(); 247 logger.log("Received request %s: %s", request.id, request.method); 248 249 if (_initialized || request.method == "initialize") 250 { 251 send(request.id, 252 handler!RequestHandler(request.method)(request.params)); 253 } 254 else 255 { 256 sendError(ErrorCodes.serverNotInitialized, request, JSONValue()); 257 } 258 } 259 else 260 { 261 notification = convertFromJSON!NotificationMessage(json); 262 logger.log("Received notification: %s", notification.method); 263 264 if (_initialized || notification.method == "exit") 265 { 266 handler!NotificationHandler(notification.method)(notification.params); 267 } 268 } 269 } 270 else 271 { 272 auto response = convertFromJSON!ResponseMessage(json); 273 logger.log("Received response for request %s", response.id); 274 275 if (response.error.isNull) 276 { 277 handler!ResponseHandler(response.id.str)(response.id.str, response.result); 278 } 279 else 280 { 281 logger.error(response.error.message); 282 } 283 } 284 } 285 catch (FiberDisposedException) 286 { 287 sendError(ErrorCodes.requestCancelled, request, JSONValue()); 288 } 289 catch (JSONException e) 290 { 291 logger.error("%s: %s", ErrorCodes.parseError[0], e.toString()); 292 sendError(ErrorCodes.parseError, request, JSONValue(e.toString())); 293 } 294 catch (HandlerNotFoundException e) 295 { 296 if (notification is null || !notification.method.startsWith("$/")) 297 { 298 logger.error("%s: %s", ErrorCodes.methodNotFound[0], e.toString()); 299 sendError(ErrorCodes.methodNotFound, request, JSONValue(e.toString())); 300 } 301 } 302 catch (InvalidParamsException e) 303 { 304 logger.error("%s: %s", ErrorCodes.invalidParams[0], e.toString()); 305 sendError(ErrorCodes.invalidParams, request, JSONValue(e.toString())); 306 } 307 catch (Exception e) 308 { 309 logger.error("%s: %s", ErrorCodes.internalError[0], e.toString()); 310 sendError(ErrorCodes.internalError, request, JSONValue(e.toString())); 311 } 312 } 313 314 if (initOptions.catchErrors) 315 { 316 try 317 { 318 findAndExecuteHandler(); 319 } 320 catch (Error e) 321 { 322 logger.error("%s: %s", ErrorCodes.internalError[0], e.toString()); 323 sendError(ErrorCodes.internalError, request, JSONValue(e.toString())); 324 } 325 } 326 else 327 { 328 findAndExecuteHandler(); 329 } 330 } 331 }