Beispiel #1
0
        private void Process(IAsyncResult ar)
        {
            HttpListenerContext ctx = null;

            try
            {
                ctx = _listener.EndGetContext(ar);
            }
            catch (HttpListenerException ex) when(ex.ErrorCode == 995)  // ERROR_OPERATION_ABORTED = 995
            {
                // Request was aborted. Most likely the listener has been stopped
                // Ignore these
            }

            if (_isRunning)
            {
                _listener.BeginGetContext(Process, null);
            }

            if (ctx == null)
            {
                return;
            }

            // Process the current context
            HttpMethod requestedMethod = new HttpMethod(ctx.Request.HttpMethod);

            if (requestedMethod != HttpMethod.Get && requestedMethod != HttpMethod.Post)
            {
                ctx.Response.StatusCode = (int)HttpStatusCode.MethodNotAllowed;
                ctx.Response.OutputStream.WriteString($"Unsupported method: {requestedMethod}");
                return;
            }

            if (ctx.Request.Url.PathAndQuery == "/")
            {
                // Asked for root - send the client to the overview page

                ctx.Response.StatusCode       = (int)HttpStatusCode.TemporaryRedirect;
                ctx.Response.RedirectLocation = "/game/index.php?page=overview";

                ctx.Response.OutputStream.WriteString("Redirecting to Overview");

                // Return
                ctx.Response.Close();
                return;
            }

            // Prepare uri
            Uri targetUri = new Uri(SubstituteRoot, ctx.Request.Url.PathAndQuery);

            // NOTE: Enable this to load external ressources through proxy
            //if (targetUri.AbsolutePath.StartsWith("/SPECIAL/"))
            //{
            //    // Request is not for our target. It's for elsewhere
            //    string rest = string.Join("", targetUri.Segments.Skip(2));
            //    rest = rest.Replace(":/", "://");

            //    targetUri = new Uri(rest);
            //}

            // Prepare request
            HttpRequestMessage proxyReq = _client.BuildRequest(targetUri);

            proxyReq.Method = requestedMethod;

            if (requestedMethod == HttpMethod.Post)
            {
                MemoryStream ms = new MemoryStream();
                ctx.Request.InputStream.CopyTo(ms);
                ms.Seek(0, SeekOrigin.Begin);

                proxyReq.Content = new StreamContent(ms);
                proxyReq.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(ctx.Request.ContentType);
            }

            // Issue
            ResponseContainer resp = _client.IssueRequest(proxyReq);

            byte[] data = resp.ResponseMessage.Content.ReadAsByteArrayAsync().Sync();

            foreach (string encoding in resp.ResponseMessage.Content.Headers.ContentEncoding)
            {
                if (encoding == "gzip")
                {
                    using (var ms = new MemoryStream(data))
                        using (var gzip = new GZipStream(ms, CompressionMode.Decompress))
                            using (var msTarget = new MemoryStream())
                            {
                                gzip.CopyTo(msTarget);

                                data = msTarget.ToArray();
                            }
                }
            }

            // Rewrite html/js
            if (resp.IsHtmlResponse || resp.ResponseMessage.Content.Headers.ContentType.MediaType == "application/x-javascript")
            {
                string str = Encoding.UTF8.GetString(data);

                // NOTE: Enable this to load external ressources through proxy
                //str = Regex.Replace(str, @"(https://gf[\d]+.geo.gfsrv.net/)", $"http://{_listenHost}:{_listenPort}/SPECIAL/$0", RegexOptions.Compiled);

                str = str.Replace(SubstituteRoot.ToString().Replace("/", "\\/"), $@"http:\/\/{_listenHost}:{_listenPort}\/"); // In JS strings
                str = str.Replace(SubstituteRoot.ToString(), $"http://{_listenHost}:{_listenPort}/");                         // In links
                str = str.Replace(SubstituteRoot.Host + ":" + SubstituteRoot.Port, $"{_listenHost}:{_listenPort}");           // Without scheme
                str = str.Replace(SubstituteRoot.Host, $"{_listenHost}:{_listenPort}");                                       // Remainders

                data = Encoding.UTF8.GetBytes(str);
            }

            // Write headers
            ctx.Response.StatusCode = (int)resp.StatusCode;

            foreach (KeyValuePair <string, IEnumerable <string> > header in resp.ResponseMessage.Headers)
            {
                foreach (string value in header.Value)
                {
                    ctx.Response.AddHeader(header.Key, value);
                }
            }

            ctx.Response.ContentType = resp.ResponseMessage.Content.Headers.ContentType.ToString();

            // Write content
            ctx.Response.OutputStream.Write(data, 0, data.Length);

            // Return
            ctx.Response.Close();
        }
        private void Process(IAsyncResult ar)
        {
            HttpListenerContext ctx = null;

            try
            {
                ctx = _listener.EndGetContext(ar);
            }
            catch (HttpListenerException ex) when(ex.ErrorCode == 995)  // ERROR_OPERATION_ABORTED = 995
            {
                // Request was aborted. Most likely the listener has been stopped
                // Ignore these
            }

            if (_isRunning)
            {
                _listener.BeginGetContext(Process, null);
            }

            if (ctx == null)
            {
                return;
            }



            string host = ctx.Request.Url.DnsSafeHost;
            int    port = ctx.Request.Url.Port;

            string referer       = ctx.Request.Headers.Get("Referer");
            string requestedWith = ctx.Request.Headers.Get("X-Requested-With");
            // Process the current context
            HttpMethod requestedMethod = new HttpMethod(ctx.Request.HttpMethod);

            if (requestedMethod != HttpMethod.Get && requestedMethod != HttpMethod.Post)
            {
                ctx.Response.StatusCode = (int)HttpStatusCode.MethodNotAllowed;
                ctx.Response.OutputStream.WriteString($"Unsupported method: {requestedMethod}");
                return;
            }

            Action <string> Redirect = (location) =>
            {
                ctx.Response.StatusCode       = (int)HttpStatusCode.TemporaryRedirect;
                ctx.Response.RedirectLocation = location;

                ctx.Response.OutputStream.WriteString("Redirecting to Overview");

                // Return
                ctx.Response.Close();
            };
            string overview = "/game/index.php?page=overview";

            if (ctx.Request.Url.PathAndQuery == "/")
            {
                // Asked for root - pretend there's nothing here for public addresses, redirect to overview for local
                var localEndPoint = ctx.Request.LocalEndPoint;
                if (IsLocal(localEndPoint.Address))
                {
                    Redirect(overview);
                }
                else
                {
                    ctx.Response.Close();
                }

                return;
            }
            else if (ctx.Request.Url.PathAndQuery.StartsWith($"/{CommandPrefix}/"))
            {
                bool result = ParseAndRunCommand(ctx.Request.Url.AbsolutePath, ctx.Request.QueryString);

                if (requestedWith == "XMLHttpRequest")
                {
                    ctx.Response.StatusCode = (int)(result ? HttpStatusCode.OK : HttpStatusCode.BadRequest);
                    ctx.Response.Close();
                    return;
                }

                Redirect(referer != null ? referer : overview);
                return;
            }

            // Prepare uri
            var pathAndQuery = ctx.Request.Url.PathAndQuery;

            if (pathAndQuery.Contains("redir.php"))
            {
                pathAndQuery = pathAndQuery.Replace(Uri.EscapeDataString($"http://{host}:{port}/"), Uri.EscapeDataString(SubstituteRoot.ToString()));
            }

            Uri targetUri = new Uri(SubstituteRoot, pathAndQuery);

            // NOTE: Enable this to load external ressources through proxy
            //if (targetUri.AbsolutePath.StartsWith("/SPECIAL/"))
            //{
            //    // Request is not for our target. It's for elsewhere
            //    string rest = string.Join("", targetUri.Segments.Skip(2));
            //    rest = rest.Replace(":/", "://");

            //    targetUri = new Uri(rest);
            //}

            // Prepare request
            HttpRequestMessage proxyReq = _client.BuildRequest(targetUri);

            proxyReq.Method = requestedMethod;


            if (referer != null)
            {
                referer = referer.Replace($"http://{host}:{port}/", SubstituteRoot.ToString());
                proxyReq.Headers.TryAddWithoutValidation("Referer", referer);
            }


            if (requestedWith != null)
            {
                proxyReq.Headers.TryAddWithoutValidation("X-Requested-With", requestedWith);
            }

            if (requestedMethod == HttpMethod.Post)
            {
                proxyReq.Content = CopyPostContent(ctx.Request.InputStream);
                if (ctx.Request.ContentType != null)
                {
                    proxyReq.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(ctx.Request.ContentType);
                }
            }

            // Issue
            ResponseContainer resp = _client.IssueRequest(proxyReq);

            byte[] data = resp.ResponseMessage.Content.ReadAsByteArrayAsync().Sync();

            foreach (string encoding in resp.ResponseMessage.Content.Headers.ContentEncoding)
            {
                if (encoding == "gzip")
                {
                    using (var ms = new MemoryStream(data))
                        using (var gzip = new GZipStream(ms, CompressionMode.Decompress))
                            using (var msTarget = new MemoryStream())
                            {
                                gzip.CopyTo(msTarget);

                                data = msTarget.ToArray();
                            }
                }
            }

            // Rewrite html/js
            if (resp.IsHtmlResponse || resp.ResponseMessage.Content?.Headers?.ContentType?.MediaType == "application/x-javascript")
            {
                string str = Encoding.UTF8.GetString(data);

                // NOTE: Enable this to load external ressources through proxy
                //str = Regex.Replace(str, @"(https://gf[\d]+.geo.gfsrv.net/)", $"http://{_listenHost}:{_listenPort}/SPECIAL/$0", RegexOptions.Compiled);

                str = str.Replace(SubstituteRoot.ToString().Replace("/", "\\/"), $@"http:\/\/{host}:{port}\/"); // In JS strings
                str = str.Replace(SubstituteRoot.ToString(), $"http://{host}:{port}/");                         // In links
                str = str.Replace(SubstituteRoot.Host + ":" + SubstituteRoot.Port, $"{host}:{port}");           // Without scheme
                str = str.Replace(SubstituteRoot.Host, $"{host}:{port}");                                       // Remainders
                // To make overlays work, there is a check against ogameUrl in javascript
                str = _client.Inject(str, resp, host, port);

                data = Encoding.UTF8.GetBytes(str);
            }

            // Write headers
            ctx.Response.StatusCode = (int)resp.StatusCode;

            foreach (KeyValuePair <string, IEnumerable <string> > header in resp.ResponseMessage.Headers)
            {
                foreach (string value in header.Value)
                {
                    ctx.Response.AddHeader(header.Key, value);
                }
            }

            ctx.Response.ContentType = resp.ResponseMessage.Content?.Headers?.ContentType?.ToString();

            // Write content
            try
            {
                ctx.Response.OutputStream.Write(data, 0, data.Length);
                ctx.Response.Close();
            }
            // Specified name is no longer available, ignore, another request was made
            catch (HttpListenerException ex) when(ex.ErrorCode == 64)
            {
            }
            catch (HttpListenerException ex)
            {
                Logger.Instance.LogException(ex);
            }
        }