source: binary-improvements/MapRendering/Web/Web.cs@ 361

Last change on this file since 361 was 360, checked in by alloc, 4 years ago

A19 code

File size: 9.2 KB
RevLine 
[230]1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Net;
5using System.Net.Sockets;
6using System.Reflection;
7using System.Text;
8using System.Threading;
[325]9using AllocsFixes.FileCache;
10using AllocsFixes.NetConnections.Servers.Web.Handlers;
[230]11using UnityEngine;
[332]12using UnityEngine.Profiling;
[230]13
[325]14namespace AllocsFixes.NetConnections.Servers.Web {
[230]15 public class Web : IConsoleServer {
[244]16 private const int GUEST_PERMISSION_LEVEL = 2000;
[325]17 public static int handlingCount;
18 public static int currentHandlers;
19 public static long totalHandlingTime = 0;
[230]20 private readonly HttpListener _listener = new HttpListener ();
[325]21 private readonly string dataFolder;
[332]22 private readonly Dictionary<string, PathHandler> handlers = new CaseInsensitiveStringDictionary<PathHandler> ();
[325]23 private readonly bool useStaticCache;
[230]24
[244]25 public ConnectionHandler connectionHandler;
26
[230]27 public Web () {
28 try {
[351]29 int webPort = GamePrefs.GetInt (EnumUtils.Parse<EnumGamePrefs> ("ControlPanelPort"));
[230]30 if (webPort < 1 || webPort > 65533) {
[244]31 Log.Out ("Webserver not started (ControlPanelPort not within 1-65533)");
[230]32 return;
33 }
[325]34
35 if (!Directory.Exists (Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location) +
36 "/webserver")) {
[230]37 Log.Out ("Webserver not started (folder \"webserver\" not found in WebInterface mod folder)");
38 return;
39 }
40
[244]41 // TODO: Read from config
42 useStaticCache = false;
43
[230]44 dataFolder = Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location) + "/webserver";
45
46 if (!HttpListener.IsSupported) {
47 Log.Out ("Webserver not started (needs Windows XP SP2, Server 2003 or later or Mono)");
48 return;
49 }
[325]50
[230]51 handlers.Add (
[325]52 "/index.htm",
53 new SimpleRedirectHandler ("/static/index.html"));
[230]54 handlers.Add (
[325]55 "/favicon.ico",
56 new SimpleRedirectHandler ("/static/favicon.ico"));
[230]57 handlers.Add (
[325]58 "/session/",
59 new SessionHandler (
[244]60 "/session/",
[325]61 dataFolder,
62 this)
[244]63 );
64 handlers.Add (
[325]65 "/userstatus",
66 new UserStatusHandler ()
[244]67 );
68 if (useStaticCache) {
69 handlers.Add (
[325]70 "/static/",
71 new StaticHandler (
[244]72 "/static/",
[325]73 dataFolder,
74 new SimpleCache (),
75 false)
[244]76 );
77 } else {
78 handlers.Add (
[325]79 "/static/",
80 new StaticHandler (
[244]81 "/static/",
[325]82 dataFolder,
83 new DirectAccess (),
84 false)
[244]85 );
86 }
[230]87
88 handlers.Add (
[238]89 "/itemicons/",
90 new ItemIconHandler (
91 "/itemicons/",
92 true)
93 );
94
95 handlers.Add (
[230]96 "/map/",
97 new StaticHandler (
98 "/map/",
[238]99 GameUtils.GetSaveGameDir () + "/map",
[230]100 MapRendering.MapRendering.GetTileCache (),
[244]101 false,
102 "web.map")
[230]103 );
104
[244]105 handlers.Add (
106 "/api/",
107 new ApiHandler ("/api/")
108 );
[230]109
[326]110 connectionHandler = new ConnectionHandler ();
[244]111
[325]112 _listener.Prefixes.Add (string.Format ("http://*:{0}/", webPort + 2));
[230]113 _listener.Start ();
114
115 SdtdConsole.Instance.RegisterServer (this);
116
[325]117 _listener.BeginGetContext (HandleRequest, _listener);
[230]118
[244]119 Log.Out ("Started Webserver on " + (webPort + 2));
[230]120 } catch (Exception e) {
121 Log.Out ("Error in Web.ctor: " + e);
122 }
123 }
124
[325]125 public void Disconnect () {
126 try {
127 _listener.Stop ();
128 _listener.Close ();
129 } catch (Exception e) {
130 Log.Out ("Error in Web.Disconnect: " + e);
131 }
132 }
133
[351]134 public void SendLine (string _line) {
135 connectionHandler.SendLine (_line);
[325]136 }
137
[351]138 public void SendLog (string _text, string _trace, LogType _type) {
[325]139 // Do nothing, handled by LogBuffer internally
140 }
141
[351]142 public static bool isSslRedirected (HttpListenerRequest _req) {
143 string proto = _req.Headers ["X-Forwarded-Proto"];
[325]144 if (!string.IsNullOrEmpty (proto)) {
145 return proto.Equals ("https", StringComparison.OrdinalIgnoreCase);
146 }
147
148 return false;
149 }
[332]150
151 private readonly Version HttpProtocolVersion = new Version(1, 1);
152
153#if ENABLE_PROFILER
154 private readonly CustomSampler authSampler = CustomSampler.Create ("Auth");
155 private readonly CustomSampler handlerSampler = CustomSampler.Create ("Handler");
156#endif
[325]157
[351]158 private void HandleRequest (IAsyncResult _result) {
[326]159 if (!_listener.IsListening) {
160 return;
161 }
[325]162
[326]163 Interlocked.Increment (ref handlingCount);
164 Interlocked.Increment (ref currentHandlers);
165
[282]166// MicroStopwatch msw = new MicroStopwatch ();
[332]167#if ENABLE_PROFILER
168 Profiler.BeginThreadProfiling ("AllocsMods", "WebRequest");
[351]169 HttpListenerContext ctx = _listener.EndGetContext (_result);
[332]170 try {
171#else
[352]172 HttpListenerContext ctx = _listener.EndGetContext (_result);
[326]173 _listener.BeginGetContext (HandleRequest, _listener);
[332]174#endif
[326]175 try {
176 HttpListenerRequest request = ctx.Request;
177 HttpListenerResponse response = ctx.Response;
178 response.SendChunked = false;
[230]179
[332]180 response.ProtocolVersion = HttpProtocolVersion;
[230]181
[326]182 WebConnection conn;
[332]183#if ENABLE_PROFILER
184 authSampler.Begin ();
185#endif
[326]186 int permissionLevel = DoAuthentication (request, out conn);
[332]187#if ENABLE_PROFILER
188 authSampler.End ();
189#endif
[244]190
191
[326]192 //Log.Out ("Login status: conn!=null: {0}, permissionlevel: {1}", conn != null, permissionLevel);
[244]193
194
[326]195 if (conn != null) {
196 Cookie cookie = new Cookie ("sid", conn.SessionID, "/");
197 cookie.Expired = false;
198 cookie.Expires = new DateTime (2020, 1, 1);
199 cookie.HttpOnly = true;
200 cookie.Secure = false;
201 response.AppendCookie (cookie);
202 }
[244]203
[326]204 // No game yet -> fail request
205 if (GameManager.Instance.World == null) {
206 response.StatusCode = (int) HttpStatusCode.ServiceUnavailable;
207 return;
208 }
[286]209
[326]210 if (request.Url.AbsolutePath.Length < 2) {
211 handlers ["/index.htm"].HandleRequest (request, response, conn, permissionLevel);
212 return;
213 } else {
214 foreach (KeyValuePair<string, PathHandler> kvp in handlers) {
215 if (request.Url.AbsolutePath.StartsWith (kvp.Key)) {
216 if (!kvp.Value.IsAuthorizedForHandler (conn, permissionLevel)) {
217 response.StatusCode = (int) HttpStatusCode.Forbidden;
218 if (conn != null) {
219 //Log.Out ("Web.HandleRequest: user '{0}' not allowed to access '{1}'", conn.SteamID, kvp.Value.ModuleName);
[230]220 }
[326]221 } else {
[332]222#if ENABLE_PROFILER
223 handlerSampler.Begin ();
224#endif
[326]225 kvp.Value.HandleRequest (request, response, conn, permissionLevel);
[332]226#if ENABLE_PROFILER
227 handlerSampler.End ();
228#endif
[326]229 }
[325]230
[326]231 return;
[230]232 }
[244]233 }
[326]234 }
[230]235
[326]236 // Not really relevant for non-debugging purposes:
237 //Log.Out ("Error in Web.HandleRequest(): No handler found for path \"" + request.Url.AbsolutePath + "\"");
238 response.StatusCode = (int) HttpStatusCode.NotFound;
239 } catch (IOException e) {
240 if (e.InnerException is SocketException) {
241 Log.Out ("Error in Web.HandleRequest(): Remote host closed connection: " +
242 e.InnerException.Message);
243 } else {
244 Log.Out ("Error (IO) in Web.HandleRequest(): " + e);
245 }
246 } catch (Exception e) {
[360]247 Log.Error ("Error in Web.HandleRequest(): ");
248 Log.Exception (e);
[326]249 } finally {
250 if (ctx != null && !ctx.Response.SendChunked) {
251 ctx.Response.Close ();
252 }
[325]253
[282]254// msw.Stop ();
255// totalHandlingTime += msw.ElapsedMicroseconds;
256// Log.Out ("Web.HandleRequest(): Took {0} µs", msw.ElapsedMicroseconds);
[326]257 Interlocked.Decrement (ref currentHandlers);
[230]258 }
[332]259#if ENABLE_PROFILER
260 } finally {
261 _listener.BeginGetContext (HandleRequest, _listener);
262 Profiler.EndThreadProfiling ();
263 }
264#endif
[230]265 }
266
[244]267 private int DoAuthentication (HttpListenerRequest _req, out WebConnection _con) {
268 _con = null;
269
270 string sessionId = null;
271 if (_req.Cookies ["sid"] != null) {
272 sessionId = _req.Cookies ["sid"].Value;
[230]273 }
[244]274
275 if (!string.IsNullOrEmpty (sessionId)) {
[332]276 WebConnection con = connectionHandler.IsLoggedIn (sessionId, _req.RemoteEndPoint.Address);
[244]277 if (con != null) {
278 _con = con;
[360]279 return GameManager.Instance.adminTools.GetUserPermissionLevel (_con.SteamID.ToString ());
[244]280 }
281 }
282
283 if (_req.QueryString ["adminuser"] != null && _req.QueryString ["admintoken"] != null) {
[325]284 WebPermissions.AdminToken admin = WebPermissions.Instance.GetWebAdmin (_req.QueryString ["adminuser"],
285 _req.QueryString ["admintoken"]);
[244]286 if (admin != null) {
287 return admin.permissionLevel;
288 }
[325]289
290 Log.Warning ("Invalid Admintoken used from " + _req.RemoteEndPoint);
[244]291 }
292
[332]293 if (_req.Url.AbsolutePath.StartsWith ("/session/verify", StringComparison.OrdinalIgnoreCase)) {
[314]294 try {
295 ulong id = OpenID.Validate (_req);
296 if (id > 0) {
[332]297 WebConnection con = connectionHandler.LogIn (id, _req.RemoteEndPoint.Address);
[314]298 _con = con;
[360]299 int level = GameManager.Instance.adminTools.GetUserPermissionLevel (id.ToString ());
[325]300 Log.Out ("Steam OpenID login from {0} with ID {1}, permission level {2}",
301 _req.RemoteEndPoint.ToString (), con.SteamID, level);
[314]302 return level;
303 }
[325]304
305 Log.Out ("Steam OpenID login failed from {0}", _req.RemoteEndPoint.ToString ());
[314]306 } catch (Exception e) {
307 Log.Error ("Error validating login:");
308 Log.Exception (e);
[244]309 }
310 }
311
312 return GUEST_PERMISSION_LEVEL;
[230]313 }
314
[351]315 public static void SetResponseTextContent (HttpListenerResponse _resp, string _text) {
316 byte[] buf = Encoding.UTF8.GetBytes (_text);
317 _resp.ContentLength64 = buf.Length;
318 _resp.ContentType = "text/html";
319 _resp.ContentEncoding = Encoding.UTF8;
320 _resp.OutputStream.Write (buf, 0, buf.Length);
[230]321 }
322 }
[325]323}
Note: See TracBrowser for help on using the repository browser.