-
Notifications
You must be signed in to change notification settings - Fork 18k
net/http: FileServer is limited to only 2 concurrent file transfers due to TransmitFile limitation on Windows #73746
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Great investigation and thank you very much for this filing @shibijm. Kindly pinging @alexbrainman @qmuntal. |
It is possible to determine that we are running on client version of Windows? If yes, maybe we could avoid using Alex |
One option is the It's being used in go/src/internal/syscall/windows/version_windows.go Lines 24 to 34 in 6425749
There is also a wrapper for it in
|
Looks reasonable. Thank you. Alex |
@shibijm could you perhaps send a change list for this which will spark off the code review, if possible? Thank you. |
Change https://go.dev/cl/673855 mentions this issue: |
Go version
go version go1.24.3 windows/amd64
Output of
go env
in your module/workspace:What did you do?
Start a simple HTTP static file server on Windows using the above code.
Then, initiate more than 2 concurrent requests using
curl
or any other HTTP client.What did you see happen?
It can be seen that only 2 of the 4 requests, #1 and #2, are receiving data. #3 and #4 are stuck after receiving 512 bytes (presumably the HTTP headers). They remain pending until one of the active transfers complete.
These transfers can be initiated from the same device or multiple different devices. This is a server-side limit. The server cannot concurrently transmit file data to more than 2 requests at a time.
I have identified the root cause. Here's a rough trace of it:
*http.fileHandler.ServeHTTP
(net/http/fs.go)http.serveFile
http.serveContent
io.CopyN
(io/io.go)io.Copy
io.copyBuffer
io.copyBuffer
,src
is a*io.LimitedReader
for the file, anddst
is a*http.response
dst
has aReadFrom
function, this exit path is taken:if rf, ok := dst.(ReaderFrom); ok { return rf.ReadFrom(src) }
*http.response.ReadFrom
(net/http/server.go).ReadFrom(src)
onw.conn.rwc
, the underlying connection, a*net.TCPConn
*net.TCPConn.ReadFrom
(net/tcpsock.go)*net.TCPConn.readFrom
(net/tcpsock_posix.go)net.sendFile
(net/sendfile_windows.go)poll.SendFile
(internal/poll/sendfile_windows.go)syscall.TransmitFile
(syscall/zsyscall_windows.go)TransmitFile
inmswsock.dll
Ultimately,
TransmitFile
from Windows API is called.Here's the documentation for that function: https://learn.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile
Under remarks, the following is stated:
This limitation is the cause of this issue.
Note that this is not a problem on server versions of Windows:
What did you expect to see?
All requests should concurrently receive data.
If I edit
C:\Program Files\Go\src\net\sendfile_windows.go
to make thenet.sendFile
function immediately return0, nil, false
, theTransmitFile
call is avoided. The caller (*net.TCPConn.readFrom
) falls back to other methods of transferring the file and all HTTP requests receive data concurrently as expected.I think the solution for this would be to change the standard library's behaviour to avoid using the
TransmitFile
Windows API call while running on workstation and client versions of Windows.The text was updated successfully, but these errors were encountered: