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; }
/// <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(); }
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); }
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); } }
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); }
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); }
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); }
/// <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}";
/// <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; }
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; }
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; }
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; }
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; }