http2: support SETTINGS_NO_RFC7540_PRIORITIES in SETTINGS frame

To make sure that clients do not unnecessarily send RFC 7540 priority
signals when it would be treated as a no-op, this change makes it so
that our server always sends SETTINGS_NO_RFC7540_PRIORITIES in our
SETTINGS frame when our write scheduler is set to anything other than
the RFC 7540 write scheduler.

For golang/go#75500

Change-Id: I7a54251022087319999deda7efb663f8b251aa95
Reviewed-on: https://go-review.googlesource.com/c/net/+/729141
Reviewed-by: Damien Neil <dneil@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Nicholas Husin <husin@google.com>
This commit is contained in:
Nicholas S. Husin
2025-11-25 13:52:06 -05:00
committed by Nicholas Husin
parent 8a4d9c198f
commit 8f003b3712
3 changed files with 71 additions and 0 deletions

View File

@@ -169,6 +169,7 @@ const (
SettingMaxFrameSize SettingID = 0x5
SettingMaxHeaderListSize SettingID = 0x6
SettingEnableConnectProtocol SettingID = 0x8
SettingNoRFC7540Priorities SettingID = 0x9
)
var settingName = map[SettingID]string{
@@ -179,6 +180,7 @@ var settingName = map[SettingID]string{
SettingMaxFrameSize: "MAX_FRAME_SIZE",
SettingMaxHeaderListSize: "MAX_HEADER_LIST_SIZE",
SettingEnableConnectProtocol: "ENABLE_CONNECT_PROTOCOL",
SettingNoRFC7540Priorities: "NO_RFC7540_PRIORITIES",
}
func (s SettingID) String() string {

View File

@@ -955,6 +955,9 @@ func (sc *serverConn) serve(conf http2Config) {
if !disableExtendedConnectProtocol {
settings = append(settings, Setting{SettingEnableConnectProtocol, 1})
}
if sc.writeSchedIgnoresRFC7540() {
settings = append(settings, Setting{SettingNoRFC7540Priorities, 1})
}
sc.writeFrame(FrameWriteRequest{
write: settings,
})
@@ -1822,6 +1825,10 @@ func (sc *serverConn) processSetting(s Setting) error {
case SettingEnableConnectProtocol:
// Receipt of this parameter by a server does not
// have any impact
case SettingNoRFC7540Priorities:
if s.Val > 1 {
return ConnectionError(ErrCodeProtocol)
}
default:
// Unknown setting: "An endpoint that receives a SETTINGS
// frame with any unknown or unsupported identifier MUST

View File

@@ -5152,6 +5152,68 @@ func testServerSendDataAfterRequestBodyClose(t testing.TB) {
st.wantIdle()
}
func TestServerSettingNoRFC7540Priorities(t *testing.T) {
synctestTest(t, testServerSettingNoRFC7540Priorities)
}
func testServerSettingNoRFC7540Priorities(t testing.TB) {
tests := []struct {
ws func() WriteScheduler
wantNoRFC7540Setting bool
}{
{
ws: func() WriteScheduler {
return newPriorityWriteSchedulerRFC7540(nil)
},
wantNoRFC7540Setting: false,
},
{
ws: newPriorityWriteSchedulerRFC9218,
wantNoRFC7540Setting: true,
},
{
ws: NewRandomWriteScheduler,
wantNoRFC7540Setting: true,
},
{
ws: newRoundRobinWriteScheduler,
wantNoRFC7540Setting: true,
},
}
for _, tt := range tests {
st := newServerTester(t, nil, func(s *Server) {
s.NewWriteScheduler = tt.ws
})
defer st.Close()
var gotNoRFC7540Setting bool
st.greetAndCheckSettings(func(s Setting) error {
if s.ID != SettingNoRFC7540Priorities {
return nil
}
gotNoRFC7540Setting = s.Val == 1
return nil
})
if tt.wantNoRFC7540Setting != gotNoRFC7540Setting {
t.Errorf("want SETTINGS_NO_RFC7540_PRIORITIES to be %v, got %v", tt.wantNoRFC7540Setting, gotNoRFC7540Setting)
}
}
}
func TestServerSettingNoRFC7540PrioritiesInvalid(t *testing.T) {
synctestTest(t, testServerSettingNoRFC7540PrioritiesInvalid)
}
func testServerSettingNoRFC7540PrioritiesInvalid(t testing.TB) {
st := newServerTester(t, nil)
defer st.Close()
st.writePreface()
st.writeSettings(Setting{ID: SettingNoRFC7540Priorities, Val: 2})
synctest.Wait()
st.readFrame() // SETTINGS frame
st.readFrame() // WINDOW_UPDATE frame
st.wantGoAway(0, ErrCodeProtocol)
}
// This test documents current behavior, rather than ideal behavior that we
// would necessarily like to see. Refer to go.dev/issues/75936 for details.
func TestServerRFC7540PrioritySmallPayload(t *testing.T) {