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

This commit is contained in:
Shibi J M 2025-05-20 09:23:19 +05:30
parent c8bf388bad
commit 315ddc0cd8
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()
}