Esempio n. 1
0
        public void RequestMeshAssetOnCap(Guid capId, Guid assetId, AssetRequest.AssetRequestHandler handler, AssetRequest.AssetErrorHandler errHandler)
        {
            handler    = handler ?? throw new ArgumentNullException(nameof(handler));
            errHandler = errHandler ?? throw new ArgumentNullException(nameof(errHandler));

            if (_negativeCache != null)
            {
                _negativeCacheLock.EnterReadLock();
                var negCacheContainsId = false;
                try {
                    negCacheContainsId = _negativeCache.Contains(assetId.ToString("N"));
                }
                finally {
                    _negativeCacheLock.ExitReadLock();
                }

                if (negCacheContainsId)
                {
                    errHandler(new AssetError {
                        Error = AssetErrorType.AssetTypeWrong,
                    });
                    return;
                }
            }

            if (_caps.TryGetValue(capId, out Capability cap))
            {
                cap.RequestAsset(
                    assetId,
                    (asset) => {
                    if (!ConfigSingleton.ValidTypes.Any(type => type == asset.Type))
                    {
                        if (_negativeCache != null)
                        {
                            _negativeCacheLock.EnterWriteLock();
                            try {
                                _negativeCache.Set(new System.Runtime.Caching.CacheItem(assetId.ToString("N"), 0), _negativeCachePolicy);
                            }
                            finally {
                                _negativeCacheLock.ExitWriteLock();
                            }
                        }

                        errHandler(new AssetError {
                            Error = AssetErrorType.AssetTypeWrong,
                        });
                    }

                    if (asset.Type == 49)
                    {
                        handler(asset);
                    }
                    else
                    {
                        errHandler(new AssetError {
                            Error = AssetErrorType.AssetTypeWrong,
                        });
                    }
                },
                    errHandler
                    );

                return;
            }

            errHandler(new AssetError {
                Error = AssetErrorType.CapabilityIdUnknown,
            });
        }
Esempio n. 2
0
        private void HandleRequest(Guid assetId, AssetRequest.AssetRequestHandler handler, AssetRequest.AssetErrorHandler errHandler)
        {
            var reader = ConfigSingleton.ChattelReader;

            if (reader == null)
            {
                // There's no Chattel. Fail.
                errHandler(new AssetError {
                    Error   = AssetErrorType.ConfigIncorrect,
                    Message = "Chattel was null!",
                });
                return;
            }

            reader.GetAssetAsync(assetId, asset => {
                if (asset == null)
                {
                    errHandler(new AssetError {
                        Error   = AssetErrorType.AssetIdUnknown,
                        Message = $"Could not find any asset with ID {assetId}",
                    });
                    return;
                }

                if (!ConfigSingleton.ValidTypes.Contains(asset.Type))
                {
                    errHandler(new AssetError {
                        Error = AssetErrorType.AssetTypeWrong,
                    });
                    return;
                }

                handler(asset);
            });
        }
Esempio n. 3
0
        public void RequestAsset(Guid assetId, AssetRequest.AssetRequestHandler handler, AssetRequest.AssetErrorHandler errHandler)
        {
            if (!IsActive)               // Paused
            {
                AssetRequest requestToProcess = null;
                var          gotLock          = false;
                try {                 // Skiplock: Only locking because of possible shutdown action.
                    Monitor.TryEnter(_requests, ref gotLock);

                    if (gotLock)
                    {
                        _requests.Enqueue(new AssetRequest(assetId, handler, errHandler));

                        if (_requests.Count > MAX_QUEUED_REQUESTS)
                        {
                            _requests.TryDequeue(out requestToProcess);
                        }
                    }
                }
                finally {
                    if (gotLock)
                    {
                        Monitor.Exit(_requests);
                    }
                }

                var errors = new Queue <Exception>();                // Have to make sure both operations below happen, even if they throw.

                if (!gotLock)
                {
                    // It seems that this cap cannot queue the request. Probably shutting down. Tell the client no such luck.
                    try {
                        errHandler(new AssetError {
                            Error = AssetErrorType.QueueFilled,
                        });
                    }
                    catch (Exception e) {
                        errors.Enqueue(e);
                    }
                }

                try {
                    requestToProcess?.Respond(new AssetError {
                        Error = AssetErrorType.QueueFilled,
                    });
                }
                catch (Exception e) {
                    errors.Enqueue(e);
                }

                if (errors.Count > 0)
                {
                    throw new AggregateException(errors);
                }

                return;
            }

            // Not paused, query Chattel.
            HandleRequest(assetId, handler, errHandler);
        }
Esempio n. 4
0
        public F_StopRouter() : base("/CAPS/HTT")
        {
            Get["/TEST"] = _ => {
                LOG.Debug($"Test called by {Request.UserHostAddress}");

                return(Response.AsText("OK"));
            };

            Get["/ADDCAP/{adminToken}/{capId:guid}/{bandwidth?}"] = _ => {
                if (_.bandwidth != null && (int)_.bandwidth < 0)
                {
                    LOG.Warn($"Invalid bandwidth spec from {Request.UserHostAddress} on cap {_.capId}: bandwidth cannot be negative ({_.bandwidth})");
                    return(StockReply.BadRequest);
                }

                uint bandwidth = 0;
                if (_.bandwidth != null)
                {
                    bandwidth = _.bandwidth;
                }

                try {
                    var result = _capAdmin.AddCap(_.adminToken, _.capId, bandwidth);

                    return(result ? StockReply.Ok : StockReply.BadRequest);
                }
                catch (InvalidAdminTokenException) {
                    LOG.Warn($"Invalid admin token from {Request.UserHostAddress}");
                }

                return(StockReply.BadRequest);
            };

            Get["/REMCAP/{adminToken}/{capId:guid}"] = _ => {
                try {
                    var result = _capAdmin.RemoveCap(_.adminToken, _.capId);

                    return(result ? StockReply.Ok : StockReply.BadRequest);
                }
                catch (InvalidAdminTokenException) {
                    LOG.Warn($"Invalid admin token from {Request.UserHostAddress}");
                }

                return(StockReply.BadRequest);
            };

            Get["/PAUSE/{adminToken}/{capId:guid}"] = _ => {
                try {
                    var result = _capAdmin.PauseCap(_.adminToken, _.capId);

                    return(result ? StockReply.Ok : StockReply.BadRequest);
                }
                catch (InvalidAdminTokenException) {
                    LOG.Warn($"Invalid admin token from {Request.UserHostAddress}");
                }

                return(StockReply.BadRequest);
            };

            Get["/RESUME/{adminToken}/{capId:guid}"] = _ => {
                try {
                    var result = _capAdmin.ResumeCap(_.adminToken, _.capId);

                    return(result ? StockReply.Ok : StockReply.BadRequest);
                }
                catch (InvalidAdminTokenException) {
                    LOG.Warn($"Invalid admin token from {Request.UserHostAddress}");
                }

                return(StockReply.BadRequest);
            };

            Get["/LIMIT/{adminToken}/{capId:guid}/{bandwidth?}"] = _ => {
                if (_.bandwidth != null && (int)_.bandwidth < 0)
                {
                    LOG.Warn($"Invalid bandwidth spec from {Request.UserHostAddress} on cap {_.capId}: bandwidth cannot be negative ({_.bandwidth})");
                    return(StockReply.BadRequest);
                }

                uint bandwidth = 0;
                if (_.bandwidth != null)
                {
                    bandwidth = _.bandwidth;
                }

                try {
                    var result = _capAdmin.LimitCap(_.adminToken, _.capId, bandwidth);

                    return(result ? StockReply.Ok : StockReply.BadRequest);
                }
                catch (InvalidAdminTokenException) {
                    LOG.Warn($"Invalid admin token from {Request.UserHostAddress}");
                }

                return(StockReply.BadRequest);
            };

            Get["/{capId:guid}", true] = async(_, ct) => {
                var textureId = (Guid?)Request.Query["texture_id"];
                var meshId    = (Guid?)Request.Query["mesh_id"];

                if (textureId == null && meshId == null)
                {
                    LOG.Warn($"Bad request for asset from {Request.UserHostAddress}: mesh_id nor texture_id supplied");
                    return(StockReply.BadRequest);
                }

                if (textureId != null && meshId != null)
                {
                    // Difference from Aperture: Aperture continues and only uses the texture_id when both are specc'd.
                    LOG.Warn($"Bad request for asset from {Request.UserHostAddress}: both mesh_id and texture_id supplied");
                    return(StockReply.BadRequest);
                }

                if (textureId == Guid.Empty || meshId == Guid.Empty)
                {
                    var type = textureId != null ? "texture" : "mesh";
                    LOG.Warn($"Bad request for asset from {Request.UserHostAddress}: requested {type} is a zero guid.");
                    return(StockReply.BadRequest);
                }

                var    rangeHeaderVals = Request.Headers["Range"];
                string rangeHeader     = null;
                if (rangeHeaderVals.Any())
                {
                    rangeHeader = rangeHeaderVals.Aggregate((prev, next) => $"{prev},{next}");                     // Because Nancy's being too smart.
                }
                IEnumerable <Range> ranges = null;

                // Parse and store the ranges, but only if a byte range. As per RFC7233: "An origin server MUST ignore a Range header field that contains a range unit it does not understand."
                if (rangeHeader?.StartsWith("bytes=", StringComparison.Ordinal) ?? false)
                {
                    try {
                        ranges = Range.ParseRanges(rangeHeader);
                    }
                    catch (FormatException) {
                        LOG.Warn($"Bad range header for asset from {Request.UserHostAddress}. Requested header doesn't match RFC7233: {rangeHeader}");
                        return(StockReply.RangeError);
                    }
                    catch (ArgumentOutOfRangeException e) {
                        LOG.Warn($"Bad range header for asset from {Request.UserHostAddress}: {rangeHeader}", e);
                        return(StockReply.RangeError);
                    }
                }

                if ((ranges?.Count() ?? 0) > 5)                   // 5 is arbitrary.  In reality ranges should be few, lots of ranges usually mean an attack.
                {
                    LOG.Warn($"Too many ranges requested from {Request.UserHostAddress}: {rangeHeader}");
                    return(StockReply.RangeError);
                }

                var completionSource = new System.Threading.Tasks.TaskCompletionSource <Response>();

                AssetRequest.AssetErrorHandler errorHandler = error => {
                    switch (error.Error)
                    {
                    case AssetErrorType.CapabilityIdUnknown:
                        LOG.Warn($"Request on nonexistent cap from {Request.UserHostAddress} {error.Message}");
                        completionSource.SetResult(StockReply.NotFound);
                        break;

                    case AssetErrorType.AssetTypeWrong:
                        LOG.Warn($"Request for wrong kind of asset from {Request.UserHostAddress} {error.Message}");
                        completionSource.SetResult(StockReply.BadRequest);
                        break;

                    case AssetErrorType.ConfigIncorrect:
                        LOG.Warn($"Configuration incomplete! {error.Message}");
                        completionSource.SetResult(StockReply.InternalServerError);
                        break;

                    case AssetErrorType.AssetIdUnknown:
                        LOG.Warn($"Request for unknown asset from {Request.UserHostAddress} {error.Message}");
                        completionSource.SetResult(StockReply.NotFound);
                        break;

                    case AssetErrorType.QueueFilled:
                        LOG.Warn($"Request from {Request.UserHostAddress} had to be dropped because cap {_.capId} is paused and filled. {error.Message}");
                        completionSource.SetResult(StockReply.NotFound);
                        break;

                    default:
                        LOG.Warn($"Request from {Request.UserHostAddress} had unexpected error! {error.Message}");
                        completionSource.SetResult(StockReply.InternalServerError);
                        break;
                    }
                };

                try {
                    if (textureId != null)
                    {
                        _capAdmin.RequestTextureAssetOnCap(
                            (Guid)_.capId,
                            (Guid)textureId,
                            asset => {
                            var response = new Response();

                            PrepareResponse(response, asset, ranges);

                            completionSource.SetResult(response);
                        },
                            errorHandler
                            );
                    }
                    else
                    {
                        _capAdmin.RequestMeshAssetOnCap(
                            (Guid)_.capId,
                            (Guid)meshId,
                            asset => {
                            var response = new Response();

                            PrepareResponse(response, asset, ranges);

                            completionSource.SetResult(response);
                        },
                            errorHandler
                            );
                    }
                }
                catch (IndexOutOfRangeException e) {
                    LOG.Warn($"Bad range requested from {Request.UserHostAddress} for asset {textureId ?? meshId}: {rangeHeader}", e);
                    return(StockReply.RangeError);
                }

                return(await completionSource.Task);
            };
        }