net: avoid using Windows' TransmitFile on non-server machines

Windows API's TransmitFile function is limited to two concurrent
operations on workstation and client versions of Windows. This change
modifies the net.sendFile function to perform no work in such cases
so that TransmitFile is avoided.

Fixes #73746

Change-Id: Iba70d5d2758bf986e80c78254c8e9e10b39bb368
GitHub-Last-Rev: 315ddc0cd8
GitHub-Pull-Request: golang/go#73758
Reviewed-on: https://go-review.googlesource.com/c/go/+/673855
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Auto-Submit: Damien Neil <dneil@google.com>
Reviewed-by: Quim Muntal <quimmuntal@gmail.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
Shibi J M 2025-05-20 03:59:15 +00:00 committed by Gopher Robot
parent 0c7311e9ca
commit be0cc937ec
8 changed files with 72 additions and 12 deletions

View File

@ -256,3 +256,7 @@ type FILE_COMPLETION_INFORMATION struct {
Port syscall.Handle
Key uintptr
}
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw
const VER_NT_WORKSTATION = 0x0000001

View File

@ -11,28 +11,53 @@ import (
"unsafe"
)
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfow
type _OSVERSIONINFOW struct {
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw
type _OSVERSIONINFOEXW struct {
osVersionInfoSize uint32
majorVersion uint32
minorVersion uint32
buildNumber uint32
platformId uint32
csdVersion [128]uint16
servicePackMajor uint16
servicePackMinor uint16
suiteMask uint16
productType byte
reserved byte
}
// According to documentation, RtlGetVersion function always succeeds.
//sys rtlGetVersion(info *_OSVERSIONINFOW) = ntdll.RtlGetVersion
//sys rtlGetVersion(info *_OSVERSIONINFOEXW) = ntdll.RtlGetVersion
// Retrieves version information of the current Windows OS
// from the RtlGetVersion API.
func getVersionInfo() *_OSVERSIONINFOEXW {
info := _OSVERSIONINFOEXW{}
info.osVersionInfoSize = uint32(unsafe.Sizeof(info))
rtlGetVersion(&info)
return &info
}
// Version retrieves the major, minor, and build version numbers
// of the current Windows OS from the RtlGetVersion API.
func Version() (major, minor, build uint32) {
info := _OSVERSIONINFOW{}
info.osVersionInfoSize = uint32(unsafe.Sizeof(info))
rtlGetVersion(&info)
info := getVersionInfo()
return info.majorVersion, info.minorVersion, info.buildNumber
}
// SupportUnlimitedTransmitFile indicates whether the current
// Windows version's TransmitFile function imposes any
// concurrent operation limits.
// Workstation and client versions of Windows limit the number
// of concurrent TransmitFile operations allowed on the system
// to a maximum of two. Please see:
// https://learn.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile
// https://golang.org/issue/73746
var SupportUnlimitedTransmitFile = sync.OnceValue(func() bool {
info := getVersionInfo()
return info.productType != VER_NT_WORKSTATION
})
var (
supportTCPKeepAliveIdle bool
supportTCPKeepAliveInterval bool

View File

@ -539,7 +539,7 @@ func NtSetInformationFile(handle syscall.Handle, iosb *IO_STATUS_BLOCK, inBuffer
return
}
func rtlGetVersion(info *_OSVERSIONINFOW) {
func rtlGetVersion(info *_OSVERSIONINFOEXW) {
syscall.Syscall(procRtlGetVersion.Addr(), 1, uintptr(unsafe.Pointer(info)), 0, 0)
return
}

View File

@ -12,8 +12,6 @@ import (
"syscall"
)
const supportsSendfile = true
// sendFile copies the contents of r to c using the sendfile
// system call to minimize copies.
//
@ -22,6 +20,9 @@ const supportsSendfile = true
//
// if handled == false, sendFile performed no work.
func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
if !supportsSendfile() {
return 0, nil, false
}
var remain int64 = 0 // 0 writes the entire file
lr, ok := r.(*io.LimitedReader)
if ok {

View File

@ -0,0 +1,12 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build linux || (darwin && !ios) || dragonfly || freebsd || solaris
package net
// Always true except for workstation and client versions of Windows
func supportsSendfile() bool {
return true
}

View File

@ -8,7 +8,9 @@ package net
import "io"
const supportsSendfile = false
func supportsSendfile() bool {
return false
}
func sendFile(c *netFD, r io.Reader) (n int64, err error, handled bool) {
return 0, nil, false

View File

@ -31,11 +31,11 @@ const (
// expectSendfile runs f, and verifies that internal/poll.SendFile successfully handles
// a write to wantConn during f's execution.
//
// On platforms where supportsSendfile is false, expectSendfile runs f but does not
// On platforms where supportsSendfile() is false, expectSendfile runs f but does not
// expect a call to SendFile.
func expectSendfile(t *testing.T, wantConn Conn, f func()) {
t.Helper()
if !supportsSendfile {
if !supportsSendfile() {
f()
return
}

View File

@ -0,0 +1,16 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package net
import "internal/syscall/windows"
// Workstation and client versions of Windows limit the number
// of concurrent TransmitFile operations allowed on the system
// to a maximum of two. Please see:
// https://learn.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile
// https://golang.org/issue/73746
func supportsSendfile() bool {
return windows.SupportUnlimitedTransmitFile()
}