public static void SendResponse <T>([NotNull] this GameHttpContext context, T value, HttpStatusCode statusCode = HttpStatusCode.OK)
        {
            context.Response.StatusCode = (int)statusCode;
            var result = JsonSerializer.Serialize(value);

            context.SendResponseRaw(result, "application/json; charset=utf-8");
        }
        private static bool TryHandleStatic([NotNull] this GameHttpContext context, [NotNull] string path)
        {
            if (!File.Exists(path))
            {
                return(false);
            }
            var contentType           = TryGetContentType(path);
            var lastWriteTimeUtc      = File.GetLastWriteTimeUtc(path).ToString("R");
            var ifModifiedSinceString = context.Request.Headers["If-Modified-Since"];

            if (!string.IsNullOrEmpty(ifModifiedSinceString))
            {
                DateTime ifModifiedSince;
                if (DateTime.TryParseExact(ifModifiedSinceString, "R", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out ifModifiedSince))
                {
                    if (ifModifiedSince.ToUniversalTime().ToString("R") == lastWriteTimeUtc)
                    {
                        context.Response.StatusCode = (int)HttpStatusCode.NotModified;
                        return(true);
                    }
                }
            }
            context.Response.AppendHeader("Cache-Control", "public");
            context.Response.AppendHeader("Last-Modified", lastWriteTimeUtc);
            context.SendResponseRaw(File.ReadAllBytes(path), contentType);
            return(true);
        }
 public static void SendStaticFile([NotNull] this GameHttpContext context, [NotNull] string localPath)
 {
     if (!context.TryHandleStatic(localPath))
     {
         throw new HttpException(HttpStatusCode.NotFound, string.Format("Static resource '{0}' is not found", context.Request.RawUrl));
     }
 }
 public static void SendResponseRaw([NotNull] this GameHttpContext context, [CanBeNull] byte[] value, string contentType = null)
 {
     if (value != null)
     {
         if (!string.IsNullOrEmpty(contentType))
         {
             context.Response.ContentType = contentType;
         }
         var acceptEncoding = context.Request.Headers["Accept-Encoding"] ?? string.Empty;
         if (true || acceptEncoding.IndexOf("gzip", StringComparison.OrdinalIgnoreCase) >= 0)
         {
             var gzipedStream = new MemoryStream();
             using (var gzipStream = new GZipStream(gzipedStream, CompressionMode.Compress, true))
                 gzipStream.Write(value, 0, value.Length);
             if (gzipedStream.Length < value.Length)
             {
                 context.Response.AppendHeader("Content-Encoding", "gzip");
                 value = gzipedStream.ToArray();
             }
         }
         else
         {
             var headersString = new StringBuilder();
             foreach (string header in context.Request.Headers)
             {
                 headersString.AppendFormat("{0}={1}\r\n", header, context.Request.Headers[header]);
             }
             Log.Network.Warn(string.Format("Client do not accept gzip. Request: {0}. Session: {1}. Headers: {2}", context.Request.RawUrl, context.Session.SessionId, headersString));
         }
         context.Response.ContentLength64 = value.Length;
         context.Response.OutputStream.Write(value, 0, value.Length);
     }
 }
 private bool TryHandleActivity([NotNull] GameHttpContext context)
 {
     if (!string.Equals(context.Request.Url.AbsolutePath, basePath + "activity", StringComparison.OrdinalIgnoreCase))
     {
         return(false);
     }
     if (!context.GodMode)
     {
         return(false);
     }
     using (var writer = new StreamWriter(context.Response.OutputStream))
     {
         writer.WriteLine("Active requests:");
         foreach (var activeRequest in activeRequests.Values.OrderBy(x => x.Item1, StringComparer.OrdinalIgnoreCase).Where(x => !x.Item1.EndsWith("/activity", StringComparison.OrdinalIgnoreCase)).ToArray())
         {
             writer.WriteLine("{0} running for {1} ms", activeRequest.Item1, activeRequest.Item2.ElapsedMilliseconds);
         }
         writer.WriteLine();
         writer.WriteLine("Last requests:");
         foreach (var lastRequest in lastRequests.Reverse().Where(x => !x.Item1.EndsWith("/activity", StringComparison.OrdinalIgnoreCase)).ToArray())
         {
             writer.WriteLine("[{0}] {1} ms - {2}", lastRequest.Item3.ToLocalTime(), lastRequest.Item2, lastRequest.Item1);
         }
     }
     context.Response.Close();
     return(true);
 }
        public static T GetRequest <T>([NotNull] this GameHttpContext context)
        {
            var reader = new StreamReader(context.Request.InputStream);
            var data   = reader.ReadToEnd();
            var result = JsonSerializer.Deserialize <T>(data);

            return(result);
        }
        public static string GetStringParam([NotNull] this GameHttpContext context, string paramName)
        {
            var valueString = context.Request.QueryString[paramName];

            if (string.IsNullOrEmpty(valueString))
            {
                throw new HttpException(HttpStatusCode.BadRequest, string.Format("Query parameter '{0}' is not specified", paramName));
            }
            return(valueString);
        }
        public static TEnum GetEnumParam <TEnum>([NotNull] this GameHttpContext context, string paramName) where TEnum : struct
        {
            var result = context.GetOptionalEnumParam <TEnum>(paramName);

            if (!result.HasValue)
            {
                throw new HttpException(HttpStatusCode.BadRequest, string.Format("Query parameter '{0}' is not specified", paramName));
            }
            return(result.Value);
        }
        public static string TryGetCookie([NotNull] this GameHttpContext context, [NotNull] string cookieName)
        {
            var cookie = context.Request.Cookies[cookieName];

            if (cookie == null || string.IsNullOrEmpty(cookie.Value))
            {
                return(null);
            }
            return(cookie.Value);
        }
        public static bool GetBoolParam([NotNull] this GameHttpContext context, [NotNull] string paramName)
        {
            var value = context.GetOptionalBoolParam(paramName);

            if (!value.HasValue)
            {
                throw new HttpException(HttpStatusCode.BadRequest, string.Format("Query parameter '{0}' is not specified", paramName));
            }
            return(value.Value);
        }
        public static uint GetUIntParam([NotNull] this GameHttpContext context, string paramName)
        {
            var result = context.GetOptionalUIntParam(paramName);

            if (!result.HasValue)
            {
                throw new HttpException(HttpStatusCode.BadRequest, string.Format("Query parameter '{0}' is not specified", paramName));
            }
            return(result.Value);
        }
 public static void SendResponseString([NotNull] this GameHttpContext context, [CanBeNull] string value)
 {
     if (!string.IsNullOrEmpty(value))
     {
         var memoryStream = new MemoryStream();
         var writer       = new StreamWriter(memoryStream);
         writer.Write(value);
         writer.Flush();
         context.SendResponseRaw(memoryStream.ToArray(), "text/plain; charset=utf-8");
     }
 }
        public static T?TryGetCookie <T>([NotNull] this GameHttpContext context, [NotNull] string cookieName, [NotNull] TryParseDelegate <T> tryParse) where T : struct
        {
            var cookieValue = TryGetCookie(context, cookieName);
            T   result;

            if (string.IsNullOrEmpty(cookieValue) || !tryParse(cookieValue, out result))
            {
                return(null);
            }
            return(result);
        }
 public static void SendResponseRaw([NotNull] this GameHttpContext context, [CanBeNull] object value, string contentType = null)
 {
     if (!ReferenceEquals(value, null))
     {
         var memoryStream = new MemoryStream();
         var writer       = new StreamWriter(memoryStream);
         writer.Write(value);
         writer.Flush();
         context.SendResponseRaw(memoryStream.ToArray(), contentType);
     }
 }
        public static void SetCookie([NotNull] this GameHttpContext context, [NotNull] string cookieName, [NotNull] string cookieValue, bool httpOnly, bool persistent)
        {
            var header = string.Format("{0}={1}; path={2}", cookieName, cookieValue, context.BasePath);

            if (persistent)
            {
                header += "; expires=" + DateTime.Now.AddYears(1).ToString("R");
            }
            if (httpOnly)
            {
                header += "; httponly";
            }
            context.Response.AppendHeader("Set-Cookie", header);
        }
        public static int?GetOptionalIntParam([NotNull] this GameHttpContext context, string paramName)
        {
            var valueString = context.Request.QueryString[paramName];

            if (string.IsNullOrEmpty(valueString))
            {
                return(null);
            }
            int value;

            if (!int.TryParse(valueString, out value))
            {
                throw new HttpException(HttpStatusCode.BadRequest, string.Format("Query parameter '{0}' is invalid - int is expected", paramName));
            }
            return(value);
        }
        public static bool?GetOptionalBoolParam([NotNull] this GameHttpContext context, [NotNull] string paramName)
        {
            var boolString = context.Request.QueryString[paramName];

            if (string.IsNullOrEmpty(boolString))
            {
                return(null);
            }
            bool value;

            if (!bool.TryParse(boolString, out value))
            {
                throw new HttpException(HttpStatusCode.BadRequest, string.Format("Query parameter '{0}' is invalid - Boolean is expected", paramName));
            }
            return(value);
        }
        public static Guid?GetOptionalGuidParam([NotNull] this GameHttpContext context, [NotNull] string paramName)
        {
            var guidString = context.Request.QueryString[paramName];

            if (string.IsNullOrEmpty(guidString))
            {
                return(null);
            }
            Guid value;

            if (!Guid.TryParse(guidString, out value))
            {
                throw new HttpException(HttpStatusCode.BadRequest, string.Format("Query parameter '{0}' is invalid - Guid is expected", paramName));
            }
            return(value);
        }
        public static TEnum?GetOptionalEnumParam <TEnum>([NotNull] this GameHttpContext context, string paramName) where TEnum : struct
        {
            var valueString = context.Request.QueryString[paramName];

            if (string.IsNullOrEmpty(valueString))
            {
                return(null);
            }
            TEnum value;

            if (!Enum.TryParse(valueString, out value))
            {
                throw new HttpException(HttpStatusCode.BadRequest, string.Format("Query parameter '{0}' is invalid - {1} is expected", paramName, typeof(TEnum).Name));
            }
            return(value);
        }
        private static bool RequestedContentIsPublic([NotNull] GameHttpContext context)
        {
            if (context.IsRootPathRequested())
            {
                return(true);
            }
            var requestedPath = context.Request.Url.LocalPath;

            if (requestedPath.EndsWith(".js"))
            {
                return(true);
            }
            if (requestedPath.EndsWith(".css"))
            {
                return(true);
            }
            if (requestedPath.EndsWith(".ico"))
            {
                return(true);
            }
            if (requestedPath.StartsWith("fonts"))
            {
                return(true);
            }
            if (requestedPath.EndsWith("/nav"))
            {
                return(true);
            }
            if (requestedPath.EndsWith("/index"))
            {
                return(true);
            }
            if (requestedPath.EndsWith("/nav.html"))
            {
                return(true);
            }
            if (requestedPath.EndsWith("/index.html"))
            {
                return(true);
            }
            if (requestedPath.EndsWith("/tutorial.html"))
            {
                return(true);
            }
            return(false);
        }
        public static string GetOptionalStringParam([NotNull] this GameHttpContext context, string paramName)
        {
            var valueString = context.Request.QueryString[paramName];

            return(string.IsNullOrEmpty(valueString) ? null : valueString);
        }
        private void HandleRequest([NotNull] HttpListenerContext httpListenerContext)
        {
            var currentRequestId = Interlocked.Increment(ref requestId);

            try
            {
                var requestUrl = httpListenerContext.Request.RawUrl;
                Log.For(this).DebugFormat("Incoming request: {0}", requestUrl);
                var handleTime = Stopwatch.StartNew();
                activeRequests[currentRequestId] = Tuple.Create(requestUrl, handleTime);
                var context = new GameHttpContext(httpListenerContext, basePath, sessionManager, arenaState.GodModeSecret);
                if (TryHandleActivity(context))
                {
                    return;
                }
                try
                {
                    lock (context.Session)
                    {
                        if (arenaState.GodAccessOnly && !context.GodMode)
                        {
                            if (!RequestedContentIsPublic(context))
                            {
                                throw new HttpException(HttpStatusCode.Forbidden, "GodAccessOnly mode is ON");
                            }
                        }

                        var handlersThatCanHandle = handlers.Where(h => h.CanHandle(context)).ToArray();
                        if (handlersThatCanHandle.Length == 1)
                        {
                            Log.For(this).DebugFormat("Handling request with {0}: {1}", handlersThatCanHandle[0].GetType().Name, requestUrl);
                            handlersThatCanHandle[0].Handle(context);
                            context.Response.Close();
                            Log.For(this).DebugFormat("Request handled in {0} ms: {1}", handleTime.ElapsedMilliseconds, requestUrl);
                        }
                        else if (handlersThatCanHandle.Length == 0)
                        {
                            throw new HttpException(HttpStatusCode.NotImplemented, string.Format("Method '{0}' is not implemented", requestUrl));
                        }
                        else
                        {
                            throw new HttpException(HttpStatusCode.InternalServerError, string.Format("Method '{0}' can be handled with many handlers: {1}", requestUrl, string.Join(", ", handlersThatCanHandle.Select(h => h.GetType().Name))));
                        }
                    }
                }
                catch (HttpListenerException)
                {
                    throw;
                }
                catch (HttpException e)
                {
                    context.Response.Headers.Clear();
                    context.Response.ContentType = "text/plain; charset: utf-8";
                    e.WriteToResponse(context.Response);
                    context.Response.Close();
                }
                catch (Exception e)
                {
                    Log.For(this).Error("Request failed", e);
                    httpListenerContext.Response.Headers.Clear();
                    httpListenerContext.Response.ContentType = "text/plain; charset: utf-8";
                    httpListenerContext.Response.StatusCode  = (int)HttpStatusCode.InternalServerError;
                    using (var writer = new StreamWriter(httpListenerContext.Response.OutputStream))
                        writer.Write(e.ToString());
                    httpListenerContext.Response.Close();
                }
            }
            catch (HttpListenerException e)
            {
                Log.Network.Debug("HttpListener failure", e);
            }
            finally
            {
                Tuple <string, Stopwatch> lastRequest;
                if (activeRequests.TryRemove(currentRequestId, out lastRequest))
                {
                    lastRequests.Enqueue(Tuple.Create(lastRequest.Item1, lastRequest.Item2.ElapsedMilliseconds, DateTime.UtcNow - lastRequest.Item2.Elapsed));
                    while (lastRequests.Count > 100)
                    {
                        Tuple <string, long, DateTime> dummy;
                        lastRequests.TryDequeue(out dummy);
                    }
                }
            }
        }
 public static void Redirect([NotNull] this GameHttpContext context, [NotNull] string url)
 {
     context.Response.StatusCode       = (int)HttpStatusCode.Redirect;
     context.Response.RedirectLocation = url;
 }
 public static bool IsAjax([NotNull] this GameHttpContext context)
 {
     return(context.Request.Headers["X-Requested-With"] == "XMLHttpRequest");
 }
        public static bool IsRootPathRequested([NotNull] this GameHttpContext context)
        {
            var requestPath = context.Request.Url.AbsolutePath;

            return(requestPath.Equals(context.BasePath, StringComparison.OrdinalIgnoreCase) || (requestPath + "/").Equals(context.BasePath, StringComparison.OrdinalIgnoreCase));
        }