This post is applicable to any scenario where you want to simulate the return of HTTP 429 Too Many Requests messages. In my case, it is applicable to SharePoint Online as I want to test code that handles 429 errors.
See the end for a link to a code sample in GitHub.
Problem and Solution
My requirement is simple – test code that can recover itself from SharePoint Online throttling mechanisms.
If you are unfamiliar with request throttling in SharePoint Online, please read these:
- https://docs.microsoft.com/en-us/sharepoint/dev/general-development/how-to-avoid-getting-throttled-or-blocked-in-sharepoint-online
- And from PnP: https://docs.microsoft.com/en-us/sharepoint/dev/solution-guidance/handle-sharepoint-online-throttling-by-using-exponential-back-off
- Sample code with fallback: https://github.com/SharePoint/PnP/tree/master/Samples/Core.Throttling
What we don’t want to do is actually stress test SharePoint Online and play with its throttling mechanism. That’s just weak and chances are that we can’t generate enough requests to get throttled from a single developer machine. The only way to load a cloud is with another cloud, and that is expensive.
My solution isn’t complicated either – use a Fiddler extension to tamper with the response packets from SharePoint Online. What we want is to process some requests, then transform the response HTTP code and status message of others to 429, then in code we can capture true 429’s which appear to come from SharePoint Online.
My use case is to test that ExecureQueryRetry works in OfficeDev.PnP Sites Core: https://github.com/SharePoint/PnP-Sites-Core/blob/master/Core/OfficeDevPnP.Core/Extensions/ClientContextExtensions.cs#L73 So we can further develop the ExecuteQueryRetryAsync method as well.
Fiddler Extension Build and Deploy
- In order for Fiddler to pick up your extension, it needs to go in one of either folders.
- %userprofile%\documents\Fiddler2\Scripts to make the extension available to the current user. This is the “My Documents” folder
- Use %programfilesx86%\Fiddler2\Scripts to make the extension available to all users on the machine. This is the 64-bit Program Files folder. Change this to where your Fiddler application is installed.
- Your paths might differ if you have installed things differently or use a newer version.
2. You need to build a class library that has a public class which implements IFiddlerExtension. In your class library, add a reference to Fiddler.exe, straight from the folder where it is located. Your DLL will go in the \Scripts folder relative to the Fiddler.exe you are referencing.
3. You need to set a Fiddler.RequiredVersion assembly attribute somewhere in your code. I do it in my class that implements IFiddlerExtensions. I couldn’t get the extension to work without this, so I am assuming it is required.
If all of the above are true, Fiddler’s extension tab in Fiddler Options will show that your assembly is loaded.
The following post-build command on the project automates my DLL to go in the right folder:
- copy “$(TargetPath)” “%userprofile%\My Documents\Fiddler2\Scripts\$(TargetFilename)” /Y
Fiddler Extension Design – Throttling Simulation
You can implement the “public void AutoTamperResponseAfter(Session oSession)” method from the IAutoTamper2 interface. The passed Session object gives you access to the HTTP packet, amongst other things.
In my case, I would like to allow 10 packets to pass within 5 seconds, then tamper any more packets within that moving 5 second window. I implement a ConcurrentQueue collection, because you typically have multiple in-flight requests. ConcurrentQueue deals with the cross-message coordination of counters and does a perfect job here.
In the end, I modify the HTTPResponseCode and HTTPResponseStatus messages. Note that the return Content-Type must be “text/html” and the body must not be empty.
I also do simple exclusions, as I only want to throttle *.sharepoint.com requests in my use case. Change this to suit your situation.
Testing and Validation
Here it is in action. You can see that some requests get throttled as long as they are more than 10 and within a 5 second window. Then, reissued requests pass through as expected. This lets me test PnP’s ExecureQueryRetry method.
Sample Code
All of the above is uploaded on GitHub here: https://github.com/OneBitSoftware/Http429.SimulateThrottling
Feel free to fix issues or add functionality, or just say hello.
Happy throttling!