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 shared static this() 24 { 25 import dls.protocol.handlers : isHandler, pushHandler; 26 import std.algorithm : map; 27 import std.array : join, split; 28 import std.meta : AliasSeq; 29 import std.traits : hasUDA, select; 30 import std.typecons : tuple; 31 import std..string : capitalize; 32 33 foreach (modName; AliasSeq!("general", "client", "text_document", "window", "workspace")) 34 { 35 mixin("import dls.protocol.messages" ~ (modName.length ? "." ~ modName : "") ~ ";"); 36 mixin("alias mod = dls.protocol.messages" ~ (modName.length ? "." ~ modName : "") ~ ";"); 37 38 foreach (thing; __traits(allMembers, mod)) 39 { 40 mixin("alias t = " ~ thing ~ ";"); 41 42 static if (isHandler!t) 43 { 44 enum attrs = tuple(__traits(getAttributes, t)); 45 enum attrsWithDefaults = tuple(modName[0] ~ modName.split('_') 46 .map!capitalize().join()[1 .. $], thing, attrs.expand); 47 enum parts = tuple(attrsWithDefaults[attrs.length > 0 ? 2 : 0], 48 attrsWithDefaults[attrs.length > 1 ? 3 : 1]); 49 enum method = select!(parts[0].length != 0)(parts[0] ~ "/", "") ~ parts[1]; 50 51 pushHandler(method, &t); 52 } 53 } 54 } 55 } 56 57 final abstract class Server 58 { 59 import dls.protocol.interfaces : InitializeParams; 60 61 static bool initialized; 62 static bool shutdown; 63 static bool exit; 64 65 static void loop() 66 { 67 import dls.util.logger : logger; 68 import std.algorithm : findSplit; 69 import std.array : appender; 70 import std.conv : to; 71 import std.stdio : stdin; 72 import std..string : strip, stripRight; 73 74 auto lineAppender = appender!(char[]); 75 auto charBuffer = new char[1]; 76 string[string] headers; 77 string line; 78 79 while (!stdin.eof && !exit) 80 { 81 headers.clear(); 82 83 do 84 { 85 bool cr; 86 bool lf; 87 88 lineAppender.clear(); 89 90 do 91 { 92 auto res = stdin.rawRead(charBuffer); 93 94 if (res.length == 0) 95 { 96 break; 97 } 98 99 lineAppender ~= res[0]; 100 101 if (cr) 102 { 103 lf = res[0] == '\n'; 104 } 105 106 cr = res[0] == '\r'; 107 } 108 while (!lf); 109 110 line = lineAppender.data.stripRight().to!string; 111 auto parts = line.findSplit(":"); 112 113 if (parts[1].length > 0) 114 { 115 headers[parts[0].to!string] = parts[2].to!string; 116 } 117 } 118 while (line.length > 0); 119 120 if (headers.length == 0) 121 { 122 continue; 123 } 124 125 if ("Content-Length" !in headers) 126 { 127 logger.error("No valid Content-Length section in header"); 128 continue; 129 } 130 131 static char[] buffer; 132 buffer.length = headers["Content-Length"].strip().to!size_t; 133 handleJSON(stdin.rawRead(buffer)); 134 } 135 } 136 137 private static void handleJSON(in char[] content) 138 { 139 import dls.protocol.handlers : HandlerNotFoundException, 140 NotificationHandler, RequestHandler, ResponseHandler, handler; 141 import dls.protocol.jsonrpc : ErrorCodes, InvalidParamsException, 142 NotificationMessage, RequestMessage, ResponseMessage, send, 143 sendError; 144 import dls.util.json : convertFromJSON; 145 import dls.util.logger : logger; 146 import std.json : JSONException, JSONValue, parseJSON; 147 148 RequestMessage request; 149 150 try 151 { 152 const json = parseJSON(content); 153 154 if ("method" in json) 155 { 156 if ("id" in json) 157 { 158 request = convertFromJSON!RequestMessage(json); 159 160 if (!shutdown && (initialized || request.method == "initialize")) 161 { 162 send(request.id, handler!RequestHandler(request.method)(request.params)); 163 } 164 else 165 { 166 sendError(ErrorCodes.serverNotInitialized, request, JSONValue()); 167 } 168 } 169 else 170 { 171 auto notification = convertFromJSON!NotificationMessage(json); 172 173 if (initialized) 174 { 175 handler!NotificationHandler(notification.method)(notification.params); 176 } 177 } 178 } 179 else 180 { 181 auto response = convertFromJSON!ResponseMessage(json); 182 183 if (response.error.isNull) 184 { 185 handler!ResponseHandler(response.id.str)(response.id.str, response.result); 186 } 187 else 188 { 189 logger.error(response.error.message); 190 } 191 } 192 } 193 catch (JSONException e) 194 { 195 logger.errorf("%s: %s", ErrorCodes.parseError[0], e.message); 196 sendError(ErrorCodes.parseError, request, JSONValue(e.message)); 197 } 198 catch (HandlerNotFoundException e) 199 { 200 logger.errorf("%s: %s", ErrorCodes.methodNotFound[0], e.message); 201 sendError(ErrorCodes.methodNotFound, request, JSONValue(e.message)); 202 } 203 catch (InvalidParamsException e) 204 { 205 logger.errorf("%s: %s", ErrorCodes.invalidParams[0], e.message); 206 sendError(ErrorCodes.invalidParams, request, JSONValue(e.message)); 207 } 208 catch (Exception e) 209 { 210 logger.errorf("%s: %s", ErrorCodes.internalError[0], e.message); 211 sendError(ErrorCodes.internalError, request, JSONValue(e.message)); 212 } 213 } 214 }