Windows XP and Slow HTTP Requests

Tuesday, June 21, 2016 categories: http, tcp, windows, work

This week at work I was faced with a bug that manifested as Windows XP clients being slower to access parts of a web interface than Windows 7 or 10. This was strange because in Wireshark the requests looked basically the same.

It turned out that the embedded system hosting the web interface was rejecting Ethernet frames larger than 1500 bytes. This was most likely because of a misinterpretation of the MTU as referring to the frame size (at layer 2) instead of the payload size (at layer 3).

The strangest part about this issue was just how consistent the effect was across all browsers tested given a version of Windows. Windows XP took nearly 22 seconds to complete a request that took just over 2 seconds in Windows 7. The core of this behavior ended up being a side-effect of the TCP retransmission timeout.

The default retransmission timeout for Windows XP is stored in the TCPInitialRTT registry value for a given network adapter. The default value when the registry value does not exist is 3000ms. This aligned with the observation under XP that the first retransmission occurred after 3 seconds with subsequent retransmissions occurring at 6 and 12 seconds. The first retransmission was sent unfragmented while the second and third retransmissions were a fragmented version of the original frame, which was ultimately accepted. The fragmented payloads were at most 576 bytes which seemed like an interesting size but I did not investigate.

Windows 7 on the other hand appears to retry after 300 milliseconds, with subsequent retries at 600ms and 1200ms. But the same basic behavior was followed: the first retry was the full payload while the subsequent retries were fragmented into at most 576 byte payloads. The key difference appearing to be the retransmission timeout. This masked the underlying issue for Windows 7 clients.

While I did not search extensively I did not find an explanation for either the doubling back-off of the retransmissions or the 576 byte payload size.

The rather quick fix was to change the Linux driver to accept frames up to the actual size that could be handled. And introducing an error message if a frame is rejected for length reasons.