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

Last change on this file since 326 was 326, checked in by alloc, 6 years ago

More cleanup, allocation improvements

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