@@ -47,6 +47,8 @@ type Protocol struct {
4747
4848 applicationBytesReceived atomic.Int64
4949 applicationBytesSent atomic.Int64
50+
51+ byteLimit int
5052}
5153
5254// New returns a new Protocol with the specified connection and every other
@@ -61,6 +63,12 @@ func New(conn *websocket.Conn) *Protocol {
6163 }
6264}
6365
66+ // SetByteLimit sets the number of bytes sent after which a test (either download or upload) will stop.
67+ // Set the value to zero to disable the byte limit.
68+ func (p * Protocol ) SetByteLimit (value int ) {
69+ p .byteLimit = value
70+ }
71+
6472// Upgrade takes a HTTP request and upgrades the connection to WebSocket.
6573// Returns a websocket Conn if the upgrade succeeded, and an error otherwise.
6674func Upgrade (w http.ResponseWriter , r * http.Request ) (* websocket.Conn , error ) {
@@ -180,6 +188,7 @@ func (p *Protocol) receiver(ctx context.Context,
180188func (p * Protocol ) sendCounterflow (ctx context.Context ,
181189 measurerCh <- chan model.Measurement , results chan <- model.WireMeasurement ,
182190 errCh chan <- error ) {
191+ byteLimit := int64 (p .byteLimit )
183192 for {
184193 select {
185194 case <- ctx .Done ():
@@ -218,13 +227,19 @@ func (p *Protocol) sendCounterflow(ctx context.Context,
218227 case results <- wm :
219228 default :
220229 }
230+
231+ // End the test once enough bytes have been received.
232+ if byteLimit > 0 && m .TCPInfo != nil && m .TCPInfo .BytesReceived >= byteLimit {
233+ p .close (ctx )
234+ return
235+ }
221236 }
222237 }
223238}
224239
225240func (p * Protocol ) sender (ctx context.Context , measurerCh <- chan model.Measurement ,
226241 results chan <- model.WireMeasurement , errCh chan <- error ) {
227- size := spec .MinMessageSize
242+ size := p . ScaleMessage ( spec .MinMessageSize , 0 )
228243 message , err := p .makePreparedMessage (size )
229244 if err != nil {
230245 log .Printf ("makePreparedMessage failed (ctx: %p)" , ctx )
@@ -283,27 +298,39 @@ func (p *Protocol) sender(ctx context.Context, measurerCh <-chan model.Measureme
283298 }
284299 p .applicationBytesSent .Add (int64 (size ))
285300
286- // Determine whether it's time to scale the message size.
287- if size >= spec .MaxScaledMessageSize {
288- continue
301+ bytesSent := int (p .applicationBytesSent .Load ())
302+ if p .byteLimit > 0 && bytesSent >= p .byteLimit {
303+ p .close (ctx )
304+ return
289305 }
290306
291- if size > int (p .applicationBytesSent .Load ())/ spec .ScalingFraction {
307+ // Determine whether it's time to scale the message size.
308+ if size >= spec .MaxScaledMessageSize || size > bytesSent / spec .ScalingFraction {
309+ size = p .ScaleMessage (size , bytesSent )
292310 continue
293311 }
294312
295- size *= 2
313+ size = p . ScaleMessage ( size * 2 , bytesSent )
296314 message , err = p .makePreparedMessage (size )
297315 if err != nil {
298316 log .Printf ("failed to make prepared message (ctx: %p, err: %v)" , ctx , err )
299317 errCh <- err
300318 return
301319 }
302-
303320 }
304321 }
305322}
306323
324+ // ScaleMessage sets the binary message size taking into consideration byte limits.
325+ func (p * Protocol ) ScaleMessage (msgSize int , bytesSent int ) int {
326+ // Check if the next payload size will push the total number of bytes over the limit.
327+ excess := bytesSent + msgSize - p .byteLimit
328+ if p .byteLimit > 0 && excess > 0 {
329+ msgSize -= excess
330+ }
331+ return msgSize
332+ }
333+
307334func (p * Protocol ) close (ctx context.Context ) {
308335 msg := websocket .FormatCloseMessage (
309336 websocket .CloseNormalClosure , "Done sending" )
0 commit comments