private async void OnContext(Task <HttpListenerContext> task)
        {
            await task;
            HttpListenerContext context = task.Result;
            Match match = _urlMatcher.Match(context.Request.RawUrl);

            if (match.Groups.Count != 4)
            {
                context.Response.StatusCode = (int)HttpStatusCode.BadRequest;                 //Invalid response
                context.Response.Close();
                _acceptSemaphore.Release();
                return;
            }

            if (match.Groups [3].Value != Registration.HomeserverToken)
            {
                context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                context.Response.Close();
                _acceptSemaphore.Release();
            }

            string type = match.Groups [1].Value;

            context.Response.StatusCode = (int)HttpStatusCode.OK;

            //Check methods
            switch (type)
            {
            case "users":
            case "rooms":
                if (context.Request.HttpMethod != "GET")
                {
                    context.Response.StatusCode = (int)HttpStatusCode.MethodNotAllowed;
                }
                break;

            case "transactions":
                if (context.Request.HttpMethod != "PUT")
                {
                    context.Response.StatusCode = (int)HttpStatusCode.MethodNotAllowed;
                }
                break;
            }

            bool exists = false;

            if (context.Response.StatusCode == (int)HttpStatusCode.OK)
            {
                if (type == "rooms")
                {
                    context.Response.StatusCode = (int)HttpStatusCode.NotFound;
                    string alias = Uri.UnescapeDataString(match.Groups [2].Value);
                    OnAliasRequest?.Invoke(alias, out exists);
                }
                else if (type == "transactions")
                {
                    byte[] data = new byte[context.Request.ContentLength64];
                    context.Request.InputStream.Read(data, 0, data.Length);
                    ASEventBatch batch = JsonConvert.DeserializeObject <ASEventBatch> (Encoding.UTF8.GetString(data), new JSONEventConverter());
                    foreach (MatrixEvent ev in batch.events)
                    {
                        OnEvent?.Invoke(ev);
                    }
                }
                else if (type == "users")
                {
                    string user = Uri.UnescapeDataString(match.Groups [2].Value);
                    OnUserRequest?.Invoke(user, out exists);
                    context.Response.StatusCode = (int)HttpStatusCode.NotFound;
                }
            }

            if (exists)
            {
                context.Response.StatusCode = 200;
            }

            context.Response.OutputStream.Write(new byte[2] {
                123, 125
            }, 0, 2);                                                                 //{}
            context.Response.Close();
            _acceptSemaphore.Release();
        }
        private async void HttpContextHandler(Task <HttpListenerContext> task)
        {
            await task.ConfigureAwait(false);

            var context = task.Result;
            var match   = _urlMatcher.Match(context.Request.RawUrl);

            if (match.Groups.Count != 4)
            {
                context.Response.StatusCode = (int)HttpStatusCode.BadRequest;  //Invalid response
                context.Response.Close();
                _acceptSemaphore.Release();
                return;
            }

            if (match.Groups[3].Value != Registration.HomeserverToken)
            {
                context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                context.Response.Close();
                _acceptSemaphore.Release();
            }

            var type = match.Groups[1].Value;

            context.Response.StatusCode = (int)HttpStatusCode.OK;

            //Check methods
            switch (type)
            {
            case "users":
            case "rooms":
                if (context.Request.HttpMethod != "GET")
                {
                    context.Response.StatusCode = (int)HttpStatusCode.MethodNotAllowed;
                }
                break;

            case "transactions":
                if (context.Request.HttpMethod != "PUT")
                {
                    context.Response.StatusCode = (int)HttpStatusCode.MethodNotAllowed;
                }
                break;
            }

            var exists = false;

            if (context.Response.StatusCode == (int)HttpStatusCode.OK)
            {
                switch (type)
                {
                case "rooms":
                    context.Response.StatusCode = (int)HttpStatusCode.NotFound;
                    var alias = Uri.UnescapeDataString(match.Groups[2].Value);
                    OnAliasRequest?.Invoke(alias, out exists);
                    break;

                case "transactions":
                    var data = new byte[context.Request.ContentLength64];
                    context.Request.InputStream.Read(data, 0, data.Length);
                    var batch = System.Text.Json.JsonSerializer.Deserialize <EventBatch>(
                        Encoding.UTF8.GetString(data));
                    foreach (var ev in batch.Events)
                    {
                        OnEvent?.Invoke(ev);
                    }

                    break;

                case "users":
                    var user = Uri.UnescapeDataString(match.Groups[2].Value);
                    OnUserRequest?.Invoke(user, out exists);
                    context.Response.StatusCode = (int)HttpStatusCode.NotFound;
                    break;
                }
            }

            if (exists)
            {
                context.Response.StatusCode = 200;
            }

            context.Response.OutputStream.Write(new byte[] { 123, 125 }, 0, 2);
            context.Response.Close();
            _acceptSemaphore.Release();
        }