Exemple #1
0
 internal RateLimitBucket(RestRequestMethod method, string route, string guild_id, string channel_id)
 {
     this.Method    = method;
     this.Route     = route;
     this.ChannelId = channel_id;
     this.GuildId   = guild_id;
 }
Exemple #2
0
 /// <summary>
 /// Initializes a new instance of the RestRequest class.
 /// </summary>
 /// <param name="method">The HTTP method to use for the request.
 /// Possible values include: 'GET', 'POST'</param>
 /// <param name="uri">The HTTP URI to use for the request.</param>
 /// <param name="authentication">The authentication information
 /// required in the request to the health provider.</param>
 public RestRequest(RestRequestMethod method, string uri, RestRequestAuthentication authentication)
 {
     Method         = method;
     Uri            = uri;
     Authentication = authentication;
     CustomInit();
 }
Exemple #3
0
 internal RateLimitBucket(RestRequestMethod method, string route, string guild_id, string channel_id, string webhook_id)
 {
     Method    = method;
     Route     = route;
     ChannelId = channel_id;
     GuildId   = guild_id;
     WebhookId = webhook_id;
 }
        internal static string ToSerializedValue(this RestRequestMethod value)
        {
            switch (value)
            {
            case RestRequestMethod.GET:
                return("GET");

            case RestRequestMethod.POST:
                return("POST");
            }
            return(null);
        }
Exemple #5
0
        public RateLimitBucket GetBucket(RestRequestMethod method, string route, object route_params, out string url)
        {
            var rparams_props = route_params.GetType()
                                .GetTypeInfo()
                                .DeclaredProperties;
            var rparams = new Dictionary <string, string>();

            foreach (var xp in rparams_props)
            {
                var val = xp.GetValue(route_params);
                if (val is string xs)
                {
                    rparams[xp.Name] = xs;
                }
                else if (val is DateTime dt)
                {
                    rparams[xp.Name] = dt.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture);
                }
                else if (val is DateTimeOffset dto)
                {
                    rparams[xp.Name] = dto.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture);
                }
                else if (val is IFormattable xf)
                {
                    rparams[xp.Name] = xf.ToString(null, CultureInfo.InvariantCulture);
                }
                else
                {
                    rparams[xp.Name] = val.ToString();
                }
            }

            var guild_id   = rparams.ContainsKey("guild_id") ? rparams["guild_id"] : "";
            var channel_id = rparams.ContainsKey("channel_id") ? rparams["channel_id"] : "";
            var webhook_id = rparams.ContainsKey("webhook_id") ? rparams["webhook_id"] : "";

            var id = RateLimitBucket.GenerateId(method, route, guild_id, channel_id, webhook_id);

            RateLimitBucket bucket = null;

            bucket = this.Buckets.FirstOrDefault(xb => xb.BucketId == id);
            if (bucket == null)
            {
                bucket = new RateLimitBucket(method, route, guild_id, channel_id, webhook_id);
                this.Buckets.Add(bucket);
            }

            url = RouteArgumentRegex.Replace(route, xm => rparams[xm.Groups[1].Value]);
            return(bucket);
        }
        /// <summary>
        /// Creates a new <see cref="BaseRestRequest"/> with specified parameters.
        /// </summary>
        /// <param name="client"><see cref="DiscordClient"/> from which this request originated.</param>
        /// <param name="bucket">Rate limit bucket to place this request in.</param>
        /// <param name="url">Uri to which this request is going to be sent to.</param>
        /// <param name="method">Method to use for this request,</param>
        /// <param name="headers">Additional headers for this request.</param>
        /// <param name="ratelimit_wait_override">Override for ratelimit bucket wait time.</param>
        internal BaseRestRequest(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, IDictionary <string, string> headers = null, double?ratelimit_wait_override = null)
        {
            Discord           = client;
            RateLimitBucket   = bucket;
            RequestTaskSource = new TaskCompletionSource <RestResponse>();
            Url    = url;
            Method = method;
            RateLimitWaitOverride = ratelimit_wait_override;

            if (headers != null)
            {
                headers = headers.Select(x => new KeyValuePair <string, string>(x.Key, Uri.EscapeDataString(x.Value)))
                          .ToDictionary(x => x.Key, x => x.Value);
                Headers = new ReadOnlyDictionary <string, string>(headers);
            }
        }
Exemple #7
0
        public RateLimitBucket GetBucket(RestRequestMethod method, string route, object route_params, out string url)
        {
            var rparams_props = route_params.GetType()
                                .GetTypeInfo()
                                .DeclaredProperties;
            var rparams = new Dictionary <string, string>();

            foreach (var xp in rparams_props)
            {
                var val = xp.GetValue(route_params);
                if (val is string xs)
                {
                    rparams[xp.Name] = xs;
                }
                else if (val is DateTime dt)
                {
                    rparams[xp.Name] = dt.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture);
                }
                else if (val is DateTimeOffset dto)
                {
                    rparams[xp.Name] = dto.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture);
                }
                else if (val is IFormattable xf)
                {
                    rparams[xp.Name] = xf.ToString(null, CultureInfo.InvariantCulture);
                }
                else
                {
                    rparams[xp.Name] = val.ToString();
                }
            }

            var guild_id   = rparams.ContainsKey("guild_id") ? rparams["guild_id"] : "";
            var channel_id = rparams.ContainsKey("channel_id") ? rparams["channel_id"] : "";
            var webhook_id = rparams.ContainsKey("webhook_id") ? rparams["webhook_id"] : "";

            var id = RateLimitBucket.GenerateId(method, route, guild_id, channel_id, webhook_id);

            // using the GetOrAdd version with the factory has no advantages as it will allocate the delegate, closure object and bucket (if needed) instead of just the bucket
            var bucket = this.Buckets.GetOrAdd(id, new RateLimitBucket(method, route, guild_id, channel_id, webhook_id));

            url = RouteArgumentRegex.Replace(route, xm => rparams[xm.Groups[1].Value]);
            return(bucket);
        }
Exemple #8
0
        public RateLimitBucket GetBucket(RestRequestMethod method, string route, object route_params, out string url)
        {
            var rparams = route_params.GetType()
                          .GetTypeInfo()
                          .DeclaredProperties
                          .ToDictionary(xp => xp.Name, xp => xp.GetValue(route_params) as string);

            var guild_id   = rparams.ContainsKey("guild_id") ? rparams["guild_id"] : "";
            var channel_id = rparams.ContainsKey("channel_id") ? rparams["channel_id"] : "";

            var id = RateLimitBucket.GenerateId(method, route, guild_id, channel_id);

            RateLimitBucket bucket = null;

            bucket = this.Buckets.FirstOrDefault(xb => xb.BucketId == id);
            if (bucket == null)
            {
                bucket = new RateLimitBucket(method, route, guild_id, channel_id);
                this.Buckets.Add(bucket);
            }

            url = RouteArgumentRegex.Replace(route, xm => rparams[xm.Groups[1].Value]);
            return(bucket);
        }
Exemple #9
0
        public RateLimitBucket GetBucket(RestRequestMethod method, string route, object route_params, out string url)
        {
            var rparams_props = route_params.GetType()
                                .GetTypeInfo()
                                .DeclaredProperties;
            var rparams = new Dictionary <string, string>();

            foreach (var xp in rparams_props)
            {
                var val = xp.GetValue(route_params);
                if (val is string xs)
                {
                    rparams[xp.Name] = xs;
                }
                else if (val is DateTime dt)
                {
                    rparams[xp.Name] = dt.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture);
                }
                else if (val is DateTimeOffset dto)
                {
                    rparams[xp.Name] = dto.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture);
                }
                else
                {
                    rparams[xp.Name] = val is IFormattable xf?xf.ToString(null, CultureInfo.InvariantCulture) : val.ToString();
                }
            }

            var guild_id   = rparams.ContainsKey("guild_id") ? rparams["guild_id"] : "";
            var channel_id = rparams.ContainsKey("channel_id") ? rparams["channel_id"] : "";
            var webhook_id = rparams.ContainsKey("webhook_id") ? rparams["webhook_id"] : "";

            // Create a generic route (minus major params) key
            // ex: POST:/channels/channel_id/messages
            var hashKey = RateLimitBucket.GenerateHashKey(method, route);

            // We check if the hash is present, using our generic route (without major params)
            // ex: in POST:/channels/channel_id/messages, out 80c17d2f203122d936070c88c8d10f33
            // If it doesn't exist, we create an unlimited hash as our initial key in the form of the hash key + the unlimited constant
            // and assign this to the route to hash cache
            // ex: this.RoutesToHashes[POST:/channels/channel_id/messages] = POST:/channels/channel_id/messages:unlimited
            var hash = this.RoutesToHashes.GetOrAdd(hashKey, RateLimitBucket.GenerateUnlimitedHash(method, route));

            // Next we use the hash to generate the key to obtain the bucket.
            // ex: 80c17d2f203122d936070c88c8d10f33:guild_id:506128773926879242:webhook_id
            // or if unlimited: POST:/channels/channel_id/messages:unlimited:guild_id:506128773926879242:webhook_id
            var bucketId = RateLimitBucket.GenerateBucketId(hash, guild_id, channel_id, webhook_id);

            // If it's not in cache, create a new bucket and index it by its bucket id.
            var bucket = this.HashesToBuckets.GetOrAdd(bucketId, new RateLimitBucket(hash, guild_id, channel_id, webhook_id));

            bucket.LastAttemptAt = DateTimeOffset.UtcNow;

            // Cache the routes for each bucket so it can be used for GC later.
            if (!bucket.RouteHashes.Contains(bucketId))
            {
                bucket.RouteHashes.Add(bucketId);
            }

            // Add the current route to the request queue, which indexes the amount
            // of requests occurring to the bucket id.
            _ = this.RequestQueue.TryGetValue(bucketId, out var count);

            // Increment by one atomically due to concurrency
            this.RequestQueue[bucketId] = Interlocked.Increment(ref count);

            // Start bucket cleaner if not already running.
            if (!this._cleanerRunning)
            {
                this._cleanerRunning           = true;
                this._bucketCleanerTokenSource = new CancellationTokenSource();
                this._cleanerTask = Task.Run(this.CleanupBucketsAsync, this._bucketCleanerTokenSource.Token);
                this.Logger.LogDebug(LoggerEvents.RestCleaner, "Bucket cleaner task started.");
            }

            url = RouteArgumentRegex.Replace(route, xm => rparams[xm.Groups[1].Value]);
            return(bucket);
        }
Exemple #10
0
 /// <summary>
 /// Generates an ID for this request bucket.
 /// </summary>
 /// <param name="method">Method for this bucket.</param>
 /// <param name="route">Route for this bucket.</param>
 /// <param name="guild_id">Guild Id for this bucket.</param>
 /// <param name="channel_id">Channel Id for this bucket.</param>
 /// <returns>Bucket Id.</returns>
 public static string GenerateId(RestRequestMethod method, string route, string guild_id, string channel_id) =>
 $"{method}:{guild_id}:{channel_id}:{route}";
Exemple #11
0
 /// <summary>
 /// Creates a new <see cref="BaseRestRequest"/> with specified parameters.
 /// </summary>
 /// <param name="client"><see cref="DiscordClient"/> from which this request originated.</param>
 /// <param name="bucket">Rate limit bucket to place this request in.</param>
 /// <param name="url">Uri to which this request is going to be sent to.</param>
 /// <param name="method">Method to use for this request,</param>
 /// <param name="headers">Additional headers for this request.</param>
 internal BaseRestRequest(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, IDictionary <string, string> headers = null)
 {
     this.Discord           = client;
     this.RateLimitBucket   = bucket;
     this.RequestTaskSource = new TaskCompletionSource <RestResponse>();
     this.Url     = url;
     this.Method  = method;
     this.Headers = headers != null ? new ReadOnlyDictionary <string, string>(headers) : null;
 }
Exemple #12
0
 internal MultipartWebRequest(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, IDictionary <string, string> headers = null, IDictionary <string, string> values = null,
                              IDictionary <string, Stream> files = null)
     : base(client, bucket, url, method, headers)
 {
     this.Values = values != null ? new ReadOnlyDictionary <string, string>(values) : null;
     this.Files  = files != null ? new ReadOnlyDictionary <string, Stream>(files) : null;
 }
Exemple #13
0
 internal MultipartWebRequest(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary <string, string> headers = null, IReadOnlyDictionary <string, string> values = null,
                              IReadOnlyCollection <DiscordMessageFile> files = null, double?ratelimit_wait_override = null)
     : base(client, bucket, url, method, route, headers, ratelimit_wait_override)
 {
     this.Values = values;
     this.Files  = files.ToDictionary(x => x.FileName, x => x.Stream);
 }
 internal MultipartWebRequest(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary <string, string> headers = null, IReadOnlyDictionary <string, string> values = null,
                              IReadOnlyCollection <DiscordMessageFile> files = null, double?ratelimit_wait_override = null, bool removeFileCount = false)
     : base(client, bucket, url, method, route, headers, ratelimit_wait_override)
 {
     this.Values           = values;
     this.Files            = files;
     this._removeFileCount = removeFileCount;
 }
 internal MultipartWebRequest(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, IDictionary <string, string> headers = null, IDictionary <string, string> values = null,
                              IDictionary <string, Stream> files = null, double?ratelimit_wait_override = null)
     : base(client, bucket, url, method, headers, ratelimit_wait_override)
 {
     Values = values != null ? new ReadOnlyDictionary <string, string>(values) : null;
     Files  = files != null ? new ReadOnlyDictionary <string, Stream>(files) : null;
 }
Exemple #16
0
 internal RestRequest(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, IDictionary <string, string> headers = null, string payload = null)
     : base(client, bucket, url, method, headers)
 {
     this.Payload = payload;
 }
Exemple #17
0
 internal RestRequest(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, IReadOnlyDictionary <string, string> headers = null, string payload = null, double?ratelimitWaitOverride = null)
     : base(client, bucket, url, method, headers, ratelimitWaitOverride)
 {
     this.Payload = payload;
 }