|
@@ -69,6 +69,8 @@ type ThrottledConn struct {
|
|
|
net.Conn
|
|
net.Conn
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const chunkSize = 4096
|
|
|
|
|
+
|
|
|
// NewThrottledConn initializes a new ThrottledConn.
|
|
// NewThrottledConn initializes a new ThrottledConn.
|
|
|
func NewThrottledConn(conn net.Conn, limits RateLimits) *ThrottledConn {
|
|
func NewThrottledConn(conn net.Conn, limits RateLimits) *ThrottledConn {
|
|
|
|
|
|
|
@@ -117,7 +119,14 @@ func (conn *ThrottledConn) Read(buffer []byte) (int, error) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return conn.limitedReader.Read(buffer)
|
|
|
|
|
|
|
+ // When throttling, read small chunks to avoid
|
|
|
|
|
+ // excessive latency due to long waits in limitedReader.Read.
|
|
|
|
|
+
|
|
|
|
|
+ if len(buffer) <= chunkSize {
|
|
|
|
|
+ return conn.limitedReader.Read(buffer)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return conn.limitedReader.Read(buffer[0:chunkSize])
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (conn *ThrottledConn) Write(buffer []byte) (int, error) {
|
|
func (conn *ThrottledConn) Write(buffer []byte) (int, error) {
|
|
@@ -131,5 +140,26 @@ func (conn *ThrottledConn) Write(buffer []byte) (int, error) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return conn.limitedWriter.Write(buffer)
|
|
|
|
|
|
|
+ // When throttling, write buffer in small chunks to avoid
|
|
|
|
|
+ // excessive latency due to long waits in limitedWriter.Write.
|
|
|
|
|
+
|
|
|
|
|
+ bytesWritten := 0
|
|
|
|
|
+
|
|
|
|
|
+ for i := 0; i < len(buffer); i += chunkSize {
|
|
|
|
|
+
|
|
|
|
|
+ start := i
|
|
|
|
|
+ end := start + chunkSize
|
|
|
|
|
+ if end > len(buffer) {
|
|
|
|
|
+ end = len(buffer)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ n, err := conn.limitedWriter.Write(buffer[start:end])
|
|
|
|
|
+ bytesWritten += n
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ // Note: no ContextError as caller may check for io.EOF, etc.
|
|
|
|
|
+ return bytesWritten, err
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return bytesWritten, nil
|
|
|
}
|
|
}
|