We were working on a project for a company that suddenly started complaining of slow ASP.Net pages. I optimised what I could, but it seemed to me that it ran pretty fast. Then I find out that some of the customers use a slow Internet connection. The only way to test this was to simulate a slow connection.
But how can one do that on IIS 5.1, the Windows XP web server? After a while of searching I realised that it was the wrong question. I don't need this for other projects and if I did I certainly wouldn't want to slow the entire web server to check it out. Because yes, changing the metadata of the server can, supposedly, change the maximum speed the pages are delivered. But it was simply too much hassle and it wasn't a reusable solution.
My way was to create a Filter for the Response of all pages. Response.Filter is supposed to be a Stream that receives as parameter the previous Response.Filter (which at the very start is Response.OutputStream) and does something to the output of the page. So I've created a BandwidthThrottleFilter object and added it in the MasterPagePage_Load:
Create a BandwidthThrottleFilter class that inherits from the abstract class Stream
Add a constructor that receives as parameters a Stream and an integer
Add fields that will get instantiated from these two parameters
Implement all abstract methods of the Stream object and use the same methods from the Stream field
Change the Write method to also call a Delay method that receives as parameter the count parameter of the Write method
That's it. You need only create the Delay method which will do a Thread.Sleep for the duration of time it normally should take to transfer that amount of bytes. Of course, that assumes that the normal speed of transfer is negligeable.
Comments
Sorry, I was not paying attention when answering your last comment. First of all thank you for pointing out that Thread.Sleep(0) blocked the thread.
Then the answer to your question is 1000. 56K means 56000 bits. However, that doesn't mean that you can just divide the size into 125 bytes, since 56kbits refers to the entire communication in a physical device, including data, communication information, stop bits, error correction, etc.
Siderite
This comment has been removed by the author.
Siderite
The first thing I did to this was make it accept Kilobits instead of Bytes, so developers can use familiar numbers like 56 for a 56K modem.
Now my question is, when it comes to bandwidth, is it actually Kilobits (1000 bits), or Kibibits (1024 bits)?
Also, if you try to download a small file with a high bandwidth setting, you could get milliseconds=0, which would call Thread.Sleep(0) and suspend the thread!
Thanks,
- Sharpzy
Anonymous
Arghh, VB on my blog! Quick, call a priest! An exorcism must be performed :)
Thanks for the code, Evan!
Siderite
Here it is in VB.Net
The line to put in the MasterPage
Response.Filter = New BandwidthThrottleFilter(Response.Filter, 10000)
Imports System.IO
Imports System.Threading
Public Class BandwidthThrottleFilter
Inherits Stream
Private ReadOnly _bytesPerSecond As Integer
Private ReadOnly _sink As Stream
Public Sub New(ByVal sink As Stream, ByVal bytesPerSecond As Integer)
_sink = sink
_bytesPerSecond = bytesPerSecond
End Sub
Private Sub Delay(ByVal length As Integer)
Dim milliseconds As Integer = length * 1000 / _bytesPerSecond
Thread.Sleep(milliseconds)
End Sub
Public Overloads Overrides ReadOnly Property CanRead() As Boolean
Get
Return _sink.CanRead
End Get
End Property
Public Overloads Overrides ReadOnly Property CanSeek() As Boolean
Get
Return _sink.CanSeek
End Get
End Property
Public Overloads Overrides ReadOnly Property CanWrite() As Boolean
Get
Return _sink.CanWrite
End Get
End Property
Public Overloads Overrides ReadOnly Property Length() As Long
Get
Return _sink.Length
End Get
End Property
Public Overloads Overrides Property Position() As Long
Get
Return _sink.Position
End Get
Set(ByVal value As Long)
_sink.Position = value
End Set
End Property
Public Overloads Overrides Sub Flush()
_sink.Flush()
End Sub
Public Overloads Overrides Function Seek(ByVal offset As Long, ByVal origin As SeekOrigin) As Long
Return _sink.Seek(offset, origin)
End Function
Public Overloads Overrides Sub SetLength(ByVal value As Long)
_sink.SetLength(value)
End Sub
Public Overloads Overrides Function Read(ByVal buffer As Byte(), ByVal offset As Integer, ByVal count As Integer) As Integer
Return _sink.Read(buffer, offset, count)
End Function
Public Overloads Overrides Sub Write(ByVal buffer As Byte(), ByVal offset As Integer, ByVal count As Integer)
Delay(count)
_sink.Write(buffer, offset, count)
End Sub
End Class
Evan
Thanks! This was incredibly useful. I'm implementing an AJAX-like file upload and needed to artificially throttle my local bandwidth, to test the progress bar.
Your article also pointed me towards a better solution to my original problem: how to actually tell how much of the file has been uploaded.
By implementing a filter on the Request object (instead of Response) I was able to both keep track of the upload progress (simply by incrementing my counter as the data passed through), as well as being able to slow the upload down and actually see my progress bar working even on localhost.
Comments
Sorry, I was not paying attention when answering your last comment. First of all thank you for pointing out that Thread.Sleep(0) blocked the thread. Then the answer to your question is 1000. 56K means 56000 bits. However, that doesn't mean that you can just divide the size into 125 bytes, since 56kbits refers to the entire communication in a physical device, including data, communication information, stop bits, error correction, etc.
SideriteThis comment has been removed by the author.
SideriteThe first thing I did to this was make it accept Kilobits instead of Bytes, so developers can use familiar numbers like 56 for a 56K modem. Now my question is, when it comes to bandwidth, is it actually Kilobits (1000 bits), or Kibibits (1024 bits)? Also, if you try to download a small file with a high bandwidth setting, you could get milliseconds=0, which would call Thread.Sleep(0) and suspend the thread! Thanks, - Sharpzy
AnonymousArghh, VB on my blog! Quick, call a priest! An exorcism must be performed :) Thanks for the code, Evan!
SideriteHere it is in VB.Net The line to put in the MasterPage Response.Filter = New BandwidthThrottleFilter(Response.Filter, 10000) Imports System.IO Imports System.Threading Public Class BandwidthThrottleFilter Inherits Stream Private ReadOnly _bytesPerSecond As Integer Private ReadOnly _sink As Stream Public Sub New(ByVal sink As Stream, ByVal bytesPerSecond As Integer) _sink = sink _bytesPerSecond = bytesPerSecond End Sub Private Sub Delay(ByVal length As Integer) Dim milliseconds As Integer = length * 1000 / _bytesPerSecond Thread.Sleep(milliseconds) End Sub Public Overloads Overrides ReadOnly Property CanRead() As Boolean Get Return _sink.CanRead End Get End Property Public Overloads Overrides ReadOnly Property CanSeek() As Boolean Get Return _sink.CanSeek End Get End Property Public Overloads Overrides ReadOnly Property CanWrite() As Boolean Get Return _sink.CanWrite End Get End Property Public Overloads Overrides ReadOnly Property Length() As Long Get Return _sink.Length End Get End Property Public Overloads Overrides Property Position() As Long Get Return _sink.Position End Get Set(ByVal value As Long) _sink.Position = value End Set End Property Public Overloads Overrides Sub Flush() _sink.Flush() End Sub Public Overloads Overrides Function Seek(ByVal offset As Long, ByVal origin As SeekOrigin) As Long Return _sink.Seek(offset, origin) End Function Public Overloads Overrides Sub SetLength(ByVal value As Long) _sink.SetLength(value) End Sub Public Overloads Overrides Function Read(ByVal buffer As Byte(), ByVal offset As Integer, ByVal count As Integer) As Integer Return _sink.Read(buffer, offset, count) End Function Public Overloads Overrides Sub Write(ByVal buffer As Byte(), ByVal offset As Integer, ByVal count As Integer) Delay(count) _sink.Write(buffer, offset, count) End Sub End Class
EvanThanks! This was incredibly useful. I'm implementing an AJAX-like file upload and needed to artificially throttle my local bandwidth, to test the progress bar. Your article also pointed me towards a better solution to my original problem: how to actually tell how much of the file has been uploaded. By implementing a filter on the Request object (instead of Response) I was able to both keep track of the upload progress (simply by incrementing my counter as the data passed through), as well as being able to slow the upload down and actually see my progress bar working even on localhost.
serializer