This is an automated email from the ASF dual-hosted git repository.

Alanxtl pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git


The following commit(s) were added to refs/heads/develop by this push:
     new 4154395f7 feat(triple): add HTTP/3 receive window config (#3704)
4154395f7 is described below

commit 4154395f7eefd5810cf4af73d5d76847e866de76
Author: CAICAII <[email protected]>
AuthorDate: Tue Sep 1 10:13:16 2026 +0800

    feat(triple): add HTTP/3 receive window config (#3704)
    
    * feat(triple): add HTTP/3 receive window config
    
    Signed-off-by: CAICAIIs <[email protected]>
    
    * fix(triple): validate HTTP/3 receive window config
    
    Signed-off-by: CAICAIIs <[email protected]>
    
    * fix(triple): format HTTP/3 receive window test imports
    
    ---------
    
    Signed-off-by: CAICAIIs <[email protected]>
---
 global/config_test.go                              |  54 ++++--
 global/http3_config.go                             |  44 +++--
 .../triple/internal/http3config/http3_config.go    |  72 ++++++++
 .../internal/http3config/http3_config_test.go      | 195 +++++++++++++++++++--
 protocol/triple/options.go                         |  33 ++++
 protocol/triple/options_test.go                    |  79 +++++++++
 6 files changed, 440 insertions(+), 37 deletions(-)

diff --git a/global/config_test.go b/global/config_test.go
index e5e948f32..798562f3d 100644
--- a/global/config_test.go
+++ b/global/config_test.go
@@ -1166,12 +1166,16 @@ func TestDefaultTripleConfig(t *testing.T) {
 func TestHttp3ConfigClone(t *testing.T) {
        t.Run("clone_http3_config", func(t *testing.T) {
                http3 := &Http3Config{
-                       Enable:                true,
-                       Negotiation:           false,
-                       KeepAlivePeriod:       "15s",
-                       MaxIdleTimeout:        "30s",
-                       MaxIncomingStreams:    128,
-                       MaxIncomingUniStreams: 64,
+                       Enable:                         true,
+                       Negotiation:                    false,
+                       KeepAlivePeriod:                "15s",
+                       MaxIdleTimeout:                 "30s",
+                       MaxIncomingStreams:             128,
+                       MaxIncomingUniStreams:          64,
+                       InitialStreamReceiveWindow:     "524288",
+                       MaxStreamReceiveWindow:         "6291456",
+                       InitialConnectionReceiveWindow: "1048576",
+                       MaxConnectionReceiveWindow:     "16777216",
                }
                cloned := http3.Clone()
                assert.NotNil(t, cloned)
@@ -1182,6 +1186,10 @@ func TestHttp3ConfigClone(t *testing.T) {
                assert.Equal(t, http3.MaxIdleTimeout, cloned.MaxIdleTimeout)
                assert.Equal(t, http3.MaxIncomingStreams, 
cloned.MaxIncomingStreams)
                assert.Equal(t, http3.MaxIncomingUniStreams, 
cloned.MaxIncomingUniStreams)
+               assert.Equal(t, http3.InitialStreamReceiveWindow, 
cloned.InitialStreamReceiveWindow)
+               assert.Equal(t, http3.MaxStreamReceiveWindow, 
cloned.MaxStreamReceiveWindow)
+               assert.Equal(t, http3.InitialConnectionReceiveWindow, 
cloned.InitialConnectionReceiveWindow)
+               assert.Equal(t, http3.MaxConnectionReceiveWindow, 
cloned.MaxConnectionReceiveWindow)
        })
 
        t.Run("clone_nil_http3_config", func(t *testing.T) {
@@ -1201,6 +1209,10 @@ func TestHttp3ConfigClone(t *testing.T) {
                assert.Equal(t, http3.MaxIdleTimeout, cloned.MaxIdleTimeout)
                assert.Equal(t, http3.MaxIncomingStreams, 
cloned.MaxIncomingStreams)
                assert.Equal(t, http3.MaxIncomingUniStreams, 
cloned.MaxIncomingUniStreams)
+               assert.Equal(t, http3.InitialStreamReceiveWindow, 
cloned.InitialStreamReceiveWindow)
+               assert.Equal(t, http3.MaxStreamReceiveWindow, 
cloned.MaxStreamReceiveWindow)
+               assert.Equal(t, http3.InitialConnectionReceiveWindow, 
cloned.InitialConnectionReceiveWindow)
+               assert.Equal(t, http3.MaxConnectionReceiveWindow, 
cloned.MaxConnectionReceiveWindow)
        })
 }
 
@@ -1215,17 +1227,25 @@ func TestDefaultHttp3Config(t *testing.T) {
                assert.Empty(t, http3.MaxIdleTimeout)
                assert.Zero(t, http3.MaxIncomingStreams)
                assert.Zero(t, http3.MaxIncomingUniStreams)
+               assert.Empty(t, http3.InitialStreamReceiveWindow)
+               assert.Empty(t, http3.MaxStreamReceiveWindow)
+               assert.Empty(t, http3.InitialConnectionReceiveWindow)
+               assert.Empty(t, http3.MaxConnectionReceiveWindow)
        })
 }
 
 func TestHttp3ConfigJSONTags(t *testing.T) {
        http3 := &Http3Config{
-               Enable:                true,
-               Negotiation:           true,
-               KeepAlivePeriod:       "15s",
-               MaxIdleTimeout:        "30s",
-               MaxIncomingStreams:    128,
-               MaxIncomingUniStreams: 64,
+               Enable:                         true,
+               Negotiation:                    true,
+               KeepAlivePeriod:                "15s",
+               MaxIdleTimeout:                 "30s",
+               MaxIncomingStreams:             128,
+               MaxIncomingUniStreams:          64,
+               InitialStreamReceiveWindow:     "524288",
+               MaxStreamReceiveWindow:         "6291456",
+               InitialConnectionReceiveWindow: "1048576",
+               MaxConnectionReceiveWindow:     "16777216",
        }
 
        data, err := json.Marshal(http3)
@@ -1234,6 +1254,10 @@ func TestHttp3ConfigJSONTags(t *testing.T) {
        assert.Contains(t, string(data), "\"max-idle-timeout\":\"30s\"")
        assert.Contains(t, string(data), "\"max-incoming-streams\":128")
        assert.Contains(t, string(data), "\"max-incoming-uni-streams\":64")
+       assert.Contains(t, string(data), 
"\"initial-stream-receive-window\":\"524288\"")
+       assert.Contains(t, string(data), 
"\"max-stream-receive-window\":\"6291456\"")
+       assert.Contains(t, string(data), 
"\"initial-connection-receive-window\":\"1048576\"")
+       assert.Contains(t, string(data), 
"\"max-connection-receive-window\":\"16777216\"")
 
        var decoded Http3Config
        err = json.Unmarshal([]byte(`{
@@ -1242,7 +1266,11 @@ func TestHttp3ConfigJSONTags(t *testing.T) {
                "keep-alive-period": "15s",
                "max-idle-timeout": "30s",
                "max-incoming-streams": 128,
-               "max-incoming-uni-streams": 64
+               "max-incoming-uni-streams": 64,
+               "initial-stream-receive-window": "524288",
+               "max-stream-receive-window": "6291456",
+               "initial-connection-receive-window": "1048576",
+               "max-connection-receive-window": "16777216"
        }`), &decoded)
        require.NoError(t, err)
        assert.Equal(t, http3, &decoded)
diff --git a/global/http3_config.go b/global/http3_config.go
index c77a8f030..e337d53ad 100644
--- a/global/http3_config.go
+++ b/global/http3_config.go
@@ -46,17 +46,33 @@ type Http3Config struct {
 
        // MaxIncomingUniStreams defines the maximum number of concurrent 
unidirectional streams accepted by server and client.
        MaxIncomingUniStreams int64 `yaml:"max-incoming-uni-streams" 
json:"max-incoming-uni-streams,omitempty"`
+       // InitialStreamReceiveWindow defines the initial stream-level flow 
control receive window.
+       // Size strings such as "512KiB" and "6MiB" are supported.
+       InitialStreamReceiveWindow string `yaml:"initial-stream-receive-window" 
json:"initial-stream-receive-window,omitempty"`
+       // MaxStreamReceiveWindow defines the maximum stream-level flow control 
receive window.
+       // Size strings such as "512KiB" and "6MiB" are supported.
+       MaxStreamReceiveWindow string `yaml:"max-stream-receive-window" 
json:"max-stream-receive-window,omitempty"`
+       // InitialConnectionReceiveWindow defines the initial connection-level 
flow control receive window.
+       // Size strings such as "512KiB" and "15MiB" are supported.
+       InitialConnectionReceiveWindow string 
`yaml:"initial-connection-receive-window" 
json:"initial-connection-receive-window,omitempty"`
+       // MaxConnectionReceiveWindow defines the maximum connection-level flow 
control receive window.
+       // Size strings such as "512KiB" and "15MiB" are supported.
+       MaxConnectionReceiveWindow string `yaml:"max-connection-receive-window" 
json:"max-connection-receive-window,omitempty"`
 }
 
 // DefaultHttp3Config returns a default Http3Config instance.
 func DefaultHttp3Config() *Http3Config {
        return &Http3Config{
-               Enable:                false,
-               Negotiation:           true,
-               KeepAlivePeriod:       "",
-               MaxIdleTimeout:        "",
-               MaxIncomingStreams:    0,
-               MaxIncomingUniStreams: 0,
+               Enable:                         false,
+               Negotiation:                    true,
+               KeepAlivePeriod:                "",
+               MaxIdleTimeout:                 "",
+               MaxIncomingStreams:             0,
+               MaxIncomingUniStreams:          0,
+               InitialStreamReceiveWindow:     "",
+               MaxStreamReceiveWindow:         "",
+               InitialConnectionReceiveWindow: "",
+               MaxConnectionReceiveWindow:     "",
        }
 }
 
@@ -67,11 +83,15 @@ func (t *Http3Config) Clone() *Http3Config {
        }
 
        return &Http3Config{
-               Enable:                t.Enable,
-               Negotiation:           t.Negotiation,
-               KeepAlivePeriod:       t.KeepAlivePeriod,
-               MaxIdleTimeout:        t.MaxIdleTimeout,
-               MaxIncomingStreams:    t.MaxIncomingStreams,
-               MaxIncomingUniStreams: t.MaxIncomingUniStreams,
+               Enable:                         t.Enable,
+               Negotiation:                    t.Negotiation,
+               KeepAlivePeriod:                t.KeepAlivePeriod,
+               MaxIdleTimeout:                 t.MaxIdleTimeout,
+               MaxIncomingStreams:             t.MaxIncomingStreams,
+               MaxIncomingUniStreams:          t.MaxIncomingUniStreams,
+               InitialStreamReceiveWindow:     t.InitialStreamReceiveWindow,
+               MaxStreamReceiveWindow:         t.MaxStreamReceiveWindow,
+               InitialConnectionReceiveWindow: 
t.InitialConnectionReceiveWindow,
+               MaxConnectionReceiveWindow:     t.MaxConnectionReceiveWindow,
        }
 }
diff --git a/protocol/triple/internal/http3config/http3_config.go 
b/protocol/triple/internal/http3config/http3_config.go
index 89bf86964..631e872fe 100644
--- a/protocol/triple/internal/http3config/http3_config.go
+++ b/protocol/triple/internal/http3config/http3_config.go
@@ -19,11 +19,16 @@ package http3config
 
 import (
        "fmt"
+       "strconv"
+       "strings"
        "time"
 )
 
 import (
+       "github.com/dustin/go-humanize"
+
        "github.com/quic-go/quic-go"
+       "github.com/quic-go/quic-go/quicvarint"
 )
 
 import (
@@ -65,5 +70,72 @@ func NewQUICConfig(http3Config *global.Http3Config, defaults 
*quic.Config) (*qui
                quicConfig.MaxIncomingUniStreams = 
http3Config.MaxIncomingUniStreams
        }
 
+       parseReceiveWindow := func(name, value string) (uint64, error) {
+               // Parse option-generated decimal values exactly before 
accepting humanized sizes.
+               window, err := strconv.ParseUint(strings.TrimSpace(value), 10, 
64)
+               if err != nil {
+                       window, err = humanize.ParseBytes(value)
+               }
+               if err != nil {
+                       return 0, fmt.Errorf("invalid http3 %s %q: %w", name, 
value, err)
+               }
+               return window, nil
+       }
+
+       initialStreamReceiveWindow := quicConfig.InitialStreamReceiveWindow
+       maxStreamReceiveWindow := quicConfig.MaxStreamReceiveWindow
+       initialConnectionReceiveWindow := 
quicConfig.InitialConnectionReceiveWindow
+       maxConnectionReceiveWindow := quicConfig.MaxConnectionReceiveWindow
+       var err error
+       if http3Config.InitialStreamReceiveWindow != "" {
+               initialStreamReceiveWindow, err = 
parseReceiveWindow("initial-stream-receive-window", 
http3Config.InitialStreamReceiveWindow)
+               if err != nil {
+                       return nil, err
+               }
+       }
+       if http3Config.MaxStreamReceiveWindow != "" {
+               maxStreamReceiveWindow, err = 
parseReceiveWindow("max-stream-receive-window", 
http3Config.MaxStreamReceiveWindow)
+               if err != nil {
+                       return nil, err
+               }
+       }
+       if http3Config.InitialConnectionReceiveWindow != "" {
+               initialConnectionReceiveWindow, err = 
parseReceiveWindow("initial-connection-receive-window", 
http3Config.InitialConnectionReceiveWindow)
+               if err != nil {
+                       return nil, err
+               }
+       }
+       if http3Config.MaxConnectionReceiveWindow != "" {
+               maxConnectionReceiveWindow, err = 
parseReceiveWindow("max-connection-receive-window", 
http3Config.MaxConnectionReceiveWindow)
+               if err != nil {
+                       return nil, err
+               }
+       }
+
+       for _, receiveWindow := range []struct {
+               name  string
+               value uint64
+       }{
+               {"initial-stream-receive-window", initialStreamReceiveWindow},
+               {"max-stream-receive-window", maxStreamReceiveWindow},
+               {"initial-connection-receive-window", 
initialConnectionReceiveWindow},
+               {"max-connection-receive-window", maxConnectionReceiveWindow},
+       } {
+               if receiveWindow.value > quicvarint.Max {
+                       return nil, fmt.Errorf("invalid http3 %s: value %d 
exceeds QUIC varint maximum %d", receiveWindow.name, receiveWindow.value, 
quicvarint.Max)
+               }
+       }
+       if initialStreamReceiveWindow != 0 && maxStreamReceiveWindow != 0 && 
initialStreamReceiveWindow > maxStreamReceiveWindow {
+               return nil, fmt.Errorf("invalid http3 receive windows: 
initial-stream-receive-window %d exceeds max-stream-receive-window %d", 
initialStreamReceiveWindow, maxStreamReceiveWindow)
+       }
+       if initialConnectionReceiveWindow != 0 && maxConnectionReceiveWindow != 
0 && initialConnectionReceiveWindow > maxConnectionReceiveWindow {
+               return nil, fmt.Errorf("invalid http3 receive windows: 
initial-connection-receive-window %d exceeds max-connection-receive-window %d", 
initialConnectionReceiveWindow, maxConnectionReceiveWindow)
+       }
+
+       quicConfig.InitialStreamReceiveWindow = initialStreamReceiveWindow
+       quicConfig.MaxStreamReceiveWindow = maxStreamReceiveWindow
+       quicConfig.InitialConnectionReceiveWindow = 
initialConnectionReceiveWindow
+       quicConfig.MaxConnectionReceiveWindow = maxConnectionReceiveWindow
+
        return quicConfig, nil
 }
diff --git a/protocol/triple/internal/http3config/http3_config_test.go 
b/protocol/triple/internal/http3config/http3_config_test.go
index 2d3982d8c..474c1bc58 100644
--- a/protocol/triple/internal/http3config/http3_config_test.go
+++ b/protocol/triple/internal/http3config/http3_config_test.go
@@ -24,9 +24,12 @@ import (
 
 import (
        "github.com/quic-go/quic-go"
+       "github.com/quic-go/quic-go/quicvarint"
 
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
+
+       "go.yaml.in/yaml/v4"
 )
 
 import (
@@ -42,14 +45,22 @@ func TestNewQUICConfig(t *testing.T) {
                assert.Zero(t, quicConfig.MaxIdleTimeout)
                assert.Zero(t, quicConfig.MaxIncomingStreams)
                assert.Zero(t, quicConfig.MaxIncomingUniStreams)
+               assert.Zero(t, quicConfig.InitialStreamReceiveWindow)
+               assert.Zero(t, quicConfig.MaxStreamReceiveWindow)
+               assert.Zero(t, quicConfig.InitialConnectionReceiveWindow)
+               assert.Zero(t, quicConfig.MaxConnectionReceiveWindow)
        })
 
        t.Run("explicit_fields_are_mapped", func(t *testing.T) {
                quicConfig, err := NewQUICConfig(&global.Http3Config{
-                       KeepAlivePeriod:       "15s",
-                       MaxIdleTimeout:        "30s",
-                       MaxIncomingStreams:    128,
-                       MaxIncomingUniStreams: 64,
+                       KeepAlivePeriod:                "15s",
+                       MaxIdleTimeout:                 "30s",
+                       MaxIncomingStreams:             128,
+                       MaxIncomingUniStreams:          64,
+                       InitialStreamReceiveWindow:     "512KiB",
+                       MaxStreamReceiveWindow:         "6MiB",
+                       InitialConnectionReceiveWindow: "1MiB",
+                       MaxConnectionReceiveWindow:     "16MiB",
                }, nil)
                require.NoError(t, err)
                require.NotNil(t, quicConfig)
@@ -57,6 +68,10 @@ func TestNewQUICConfig(t *testing.T) {
                assert.Equal(t, 30*time.Second, quicConfig.MaxIdleTimeout)
                assert.Equal(t, int64(128), quicConfig.MaxIncomingStreams)
                assert.Equal(t, int64(64), quicConfig.MaxIncomingUniStreams)
+               assert.Equal(t, uint64(524288), 
quicConfig.InitialStreamReceiveWindow)
+               assert.Equal(t, uint64(6291456), 
quicConfig.MaxStreamReceiveWindow)
+               assert.Equal(t, uint64(1048576), 
quicConfig.InitialConnectionReceiveWindow)
+               assert.Equal(t, uint64(16777216), 
quicConfig.MaxConnectionReceiveWindow)
        })
 
        t.Run("invalid_keep_alive_period_returns_error", func(t *testing.T) {
@@ -76,10 +91,142 @@ func TestNewQUICConfig(t *testing.T) {
                assert.Nil(t, quicConfig)
                assert.ErrorContains(t, err, "max-idle-timeout")
        })
+
+       t.Run("invalid_receive_window_returns_error", func(t *testing.T) {
+               quicConfig, err := NewQUICConfig(&global.Http3Config{
+                       MaxConnectionReceiveWindow: "invalid",
+               }, nil)
+               require.Error(t, err)
+               assert.Nil(t, quicConfig)
+               assert.ErrorContains(t, err, "max-connection-receive-window")
+       })
+
+       t.Run("receive_windows_must_fit_quic_varint", func(t *testing.T) {
+               for _, test := range []struct {
+                       name  string
+                       field func(*global.Http3Config)
+                       want  string
+               }{
+                       {
+                               name: "initial_stream",
+                               field: func(config *global.Http3Config) {
+                                       config.InitialStreamReceiveWindow = 
"4611686018427387904"
+                               },
+                               want: "initial-stream-receive-window",
+                       },
+                       {
+                               name: "max_stream",
+                               field: func(config *global.Http3Config) {
+                                       config.MaxStreamReceiveWindow = 
"4611686018427387904"
+                               },
+                               want: "max-stream-receive-window",
+                       },
+                       {
+                               name: "initial_connection",
+                               field: func(config *global.Http3Config) {
+                                       config.InitialConnectionReceiveWindow = 
"4611686018427387904"
+                               },
+                               want: "initial-connection-receive-window",
+                       },
+                       {
+                               name: "max_connection",
+                               field: func(config *global.Http3Config) {
+                                       config.MaxConnectionReceiveWindow = 
"4611686018427387904"
+                               },
+                               want: "max-connection-receive-window",
+                       },
+               } {
+                       t.Run(test.name, func(t *testing.T) {
+                               config := &global.Http3Config{}
+                               test.field(config)
+
+                               quicConfig, err := NewQUICConfig(config, nil)
+                               require.Error(t, err)
+                               assert.Nil(t, quicConfig)
+                               require.ErrorContains(t, err, test.want)
+                               require.ErrorContains(t, err, "QUIC varint 
maximum")
+                       })
+               }
+       })
+
+       t.Run("initial_receive_window_must_not_exceed_maximum", func(t 
*testing.T) {
+               for _, test := range []struct {
+                       name      string
+                       configure func(*global.Http3Config)
+                       want      string
+               }{
+                       {
+                               name: "stream",
+                               configure: func(config *global.Http3Config) {
+                                       config.InitialStreamReceiveWindow = 
"16MiB"
+                                       config.MaxStreamReceiveWindow = "1MiB"
+                               },
+                               want: "initial-stream-receive-window",
+                       },
+                       {
+                               name: "connection",
+                               configure: func(config *global.Http3Config) {
+                                       config.InitialConnectionReceiveWindow = 
"32MiB"
+                                       config.MaxConnectionReceiveWindow = 
"2MiB"
+                               },
+                               want: "initial-connection-receive-window",
+                       },
+               } {
+                       t.Run(test.name, func(t *testing.T) {
+                               config := &global.Http3Config{
+                                       InitialStreamReceiveWindow:     "1MiB",
+                                       MaxStreamReceiveWindow:         "2MiB",
+                                       InitialConnectionReceiveWindow: "1MiB",
+                                       MaxConnectionReceiveWindow:     "2MiB",
+                               }
+                               test.configure(config)
+
+                               quicConfig, err := NewQUICConfig(config, nil)
+                               require.Error(t, err)
+                               assert.Nil(t, quicConfig)
+                               assert.ErrorContains(t, err, test.want)
+                       })
+               }
+       })
+
+       t.Run("quic_varint_maximum_is_accepted", func(t *testing.T) {
+               max := "4611686018427387903"
+               quicConfig, err := NewQUICConfig(&global.Http3Config{
+                       InitialStreamReceiveWindow:     max,
+                       MaxStreamReceiveWindow:         max,
+                       InitialConnectionReceiveWindow: max,
+                       MaxConnectionReceiveWindow:     max,
+               }, nil)
+               require.NoError(t, err)
+               assert.Equal(t, uint64(quicvarint.Max), 
quicConfig.InitialStreamReceiveWindow)
+               assert.Equal(t, uint64(quicvarint.Max), 
quicConfig.MaxStreamReceiveWindow)
+               assert.Equal(t, uint64(quicvarint.Max), 
quicConfig.InitialConnectionReceiveWindow)
+               assert.Equal(t, uint64(quicvarint.Max), 
quicConfig.MaxConnectionReceiveWindow)
+       })
+
+       t.Run("yaml_receive_windows_are_validated", func(t *testing.T) {
+               var config global.TripleConfig
+               err := yaml.Unmarshal([]byte(`
+http3:
+  initial-stream-receive-window: "16MiB"
+  max-stream-receive-window: "1MiB"
+  initial-connection-receive-window: "32MiB"
+  max-connection-receive-window: "2MiB"
+`), &config)
+               require.NoError(t, err)
+               _, err = NewQUICConfig(config.Http3, nil)
+               require.Error(t, err)
+               assert.ErrorContains(t, err, "initial-stream-receive-window")
+       })
+
        t.Run("nil_config_uses_defaults", func(t *testing.T) {
                defaults := &quic.Config{
-                       KeepAlivePeriod: 10 * time.Second,
-                       MaxIdleTimeout:  20 * time.Second,
+                       KeepAlivePeriod:                10 * time.Second,
+                       MaxIdleTimeout:                 20 * time.Second,
+                       InitialStreamReceiveWindow:     512 * 1024,
+                       MaxStreamReceiveWindow:         6 * 1024 * 1024,
+                       InitialConnectionReceiveWindow: 512 * 1024,
+                       MaxConnectionReceiveWindow:     15 * 1024 * 1024,
                }
 
                quicConfig, err := NewQUICConfig(nil, defaults)
@@ -88,12 +235,20 @@ func TestNewQUICConfig(t *testing.T) {
                assert.NotSame(t, defaults, quicConfig)
                assert.Equal(t, 10*time.Second, quicConfig.KeepAlivePeriod)
                assert.Equal(t, 20*time.Second, quicConfig.MaxIdleTimeout)
+               assert.Equal(t, uint64(512*1024), 
quicConfig.InitialStreamReceiveWindow)
+               assert.Equal(t, uint64(6*1024*1024), 
quicConfig.MaxStreamReceiveWindow)
+               assert.Equal(t, uint64(512*1024), 
quicConfig.InitialConnectionReceiveWindow)
+               assert.Equal(t, uint64(15*1024*1024), 
quicConfig.MaxConnectionReceiveWindow)
        })
 
        t.Run("defaults_are_used_when_fields_unset", func(t *testing.T) {
                defaults := &quic.Config{
-                       KeepAlivePeriod: 10 * time.Second,
-                       MaxIdleTimeout:  20 * time.Second,
+                       KeepAlivePeriod:                10 * time.Second,
+                       MaxIdleTimeout:                 20 * time.Second,
+                       InitialStreamReceiveWindow:     512 * 1024,
+                       MaxStreamReceiveWindow:         6 * 1024 * 1024,
+                       InitialConnectionReceiveWindow: 512 * 1024,
+                       MaxConnectionReceiveWindow:     15 * 1024 * 1024,
                }
 
                quicConfig, err := NewQUICConfig(&global.Http3Config{}, 
defaults)
@@ -102,20 +257,36 @@ func TestNewQUICConfig(t *testing.T) {
                assert.NotSame(t, defaults, quicConfig)
                assert.Equal(t, 10*time.Second, quicConfig.KeepAlivePeriod)
                assert.Equal(t, 20*time.Second, quicConfig.MaxIdleTimeout)
+               assert.Equal(t, uint64(512*1024), 
quicConfig.InitialStreamReceiveWindow)
+               assert.Equal(t, uint64(6*1024*1024), 
quicConfig.MaxStreamReceiveWindow)
+               assert.Equal(t, uint64(512*1024), 
quicConfig.InitialConnectionReceiveWindow)
+               assert.Equal(t, uint64(15*1024*1024), 
quicConfig.MaxConnectionReceiveWindow)
        })
 
        t.Run("explicit_fields_override_defaults", func(t *testing.T) {
                quicConfig, err := NewQUICConfig(&global.Http3Config{
-                       KeepAlivePeriod: "15s",
-                       MaxIdleTimeout:  "30s",
+                       KeepAlivePeriod:                "15s",
+                       MaxIdleTimeout:                 "30s",
+                       InitialStreamReceiveWindow:     "262144",
+                       MaxStreamReceiveWindow:         "4194304",
+                       InitialConnectionReceiveWindow: "524288",
+                       MaxConnectionReceiveWindow:     "8388608",
                }, &quic.Config{
-                       KeepAlivePeriod: 10 * time.Second,
-                       MaxIdleTimeout:  20 * time.Second,
+                       KeepAlivePeriod:                10 * time.Second,
+                       MaxIdleTimeout:                 20 * time.Second,
+                       InitialStreamReceiveWindow:     512 * 1024,
+                       MaxStreamReceiveWindow:         6 * 1024 * 1024,
+                       InitialConnectionReceiveWindow: 512 * 1024,
+                       MaxConnectionReceiveWindow:     15 * 1024 * 1024,
                })
                require.NoError(t, err)
                require.NotNil(t, quicConfig)
                assert.Equal(t, 15*time.Second, quicConfig.KeepAlivePeriod)
                assert.Equal(t, 30*time.Second, quicConfig.MaxIdleTimeout)
+               assert.Equal(t, uint64(262144), 
quicConfig.InitialStreamReceiveWindow)
+               assert.Equal(t, uint64(4194304), 
quicConfig.MaxStreamReceiveWindow)
+               assert.Equal(t, uint64(524288), 
quicConfig.InitialConnectionReceiveWindow)
+               assert.Equal(t, uint64(8388608), 
quicConfig.MaxConnectionReceiveWindow)
        })
 
        t.Run("explicit_zero_duration_overrides_default", func(t *testing.T) {
diff --git a/protocol/triple/options.go b/protocol/triple/options.go
index 9e1aa9276..61141658e 100644
--- a/protocol/triple/options.go
+++ b/protocol/triple/options.go
@@ -22,6 +22,7 @@ import (
        "maps"
        "net/http"
        "net/url"
+       "strconv"
        "strings"
        "time"
 )
@@ -230,6 +231,38 @@ func WithHttp3MaxIncomingUniStreams(streams int64) Option {
        }
 }
 
+// WithHttp3InitialStreamReceiveWindow sets the initial stream-level flow 
control receive window in bytes.
+// A zero window keeps the quic-go default.
+func WithHttp3InitialStreamReceiveWindow(window uint64) Option {
+       return func(opts *Options) {
+               opts.Triple.Http3.InitialStreamReceiveWindow = 
strconv.FormatUint(window, 10)
+       }
+}
+
+// WithHttp3MaxStreamReceiveWindow sets the maximum stream-level flow control 
receive window in bytes.
+// A zero window keeps the quic-go default.
+func WithHttp3MaxStreamReceiveWindow(window uint64) Option {
+       return func(opts *Options) {
+               opts.Triple.Http3.MaxStreamReceiveWindow = 
strconv.FormatUint(window, 10)
+       }
+}
+
+// WithHttp3InitialConnectionReceiveWindow sets the initial connection-level 
flow control receive window in bytes.
+// A zero window keeps the quic-go default.
+func WithHttp3InitialConnectionReceiveWindow(window uint64) Option {
+       return func(opts *Options) {
+               opts.Triple.Http3.InitialConnectionReceiveWindow = 
strconv.FormatUint(window, 10)
+       }
+}
+
+// WithHttp3MaxConnectionReceiveWindow sets the maximum connection-level flow 
control receive window in bytes.
+// A zero window keeps the quic-go default.
+func WithHttp3MaxConnectionReceiveWindow(window uint64) Option {
+       return func(opts *Options) {
+               opts.Triple.Http3.MaxConnectionReceiveWindow = 
strconv.FormatUint(window, 10)
+       }
+}
+
 // CORSOption configures a single aspect of CORS.
 type CORSOption func(*global.CorsConfig)
 
diff --git a/protocol/triple/options_test.go b/protocol/triple/options_test.go
index 61c5f1712..60667f636 100644
--- a/protocol/triple/options_test.go
+++ b/protocol/triple/options_test.go
@@ -23,10 +23,16 @@ import (
 )
 
 import (
+       "github.com/quic-go/quic-go/quicvarint"
+
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
 )
 
+import (
+       "dubbo.apache.org/dubbo-go/v3/protocol/triple/internal/http3config"
+)
+
 func TestHTTP3TransportOptions(t *testing.T) {
        opts := NewOptions(
                WithHttp3Enable(),
@@ -35,6 +41,10 @@ func TestHTTP3TransportOptions(t *testing.T) {
                WithHttp3MaxIdleTimeout(30*time.Second),
                WithHttp3MaxIncomingStreams(128),
                WithHttp3MaxIncomingUniStreams(64),
+               WithHttp3InitialStreamReceiveWindow(512*1024),
+               WithHttp3MaxStreamReceiveWindow(6*1024*1024),
+               WithHttp3InitialConnectionReceiveWindow(1024*1024),
+               WithHttp3MaxConnectionReceiveWindow(16*1024*1024),
        )
 
        require.NotNil(t, opts)
@@ -46,6 +56,75 @@ func TestHTTP3TransportOptions(t *testing.T) {
        assert.Equal(t, "30s", opts.Triple.Http3.MaxIdleTimeout)
        assert.Equal(t, int64(128), opts.Triple.Http3.MaxIncomingStreams)
        assert.Equal(t, int64(64), opts.Triple.Http3.MaxIncomingUniStreams)
+       assert.Equal(t, "524288", opts.Triple.Http3.InitialStreamReceiveWindow)
+       assert.Equal(t, "6291456", opts.Triple.Http3.MaxStreamReceiveWindow)
+       assert.Equal(t, "1048576", 
opts.Triple.Http3.InitialConnectionReceiveWindow)
+       assert.Equal(t, "16777216", 
opts.Triple.Http3.MaxConnectionReceiveWindow)
+}
+
+func TestHTTP3ReceiveWindowOptionsAreValidated(t *testing.T) {
+       for _, test := range []struct {
+               name    string
+               options []Option
+               field   string
+       }{
+               {
+                       name:    "initial_stream_overflow",
+                       options: 
[]Option{WithHttp3InitialStreamReceiveWindow(quicvarint.Max + 1)},
+                       field:   "initial-stream-receive-window",
+               },
+               {
+                       name:    "max_stream_overflow",
+                       options: 
[]Option{WithHttp3MaxStreamReceiveWindow(quicvarint.Max + 1)},
+                       field:   "max-stream-receive-window",
+               },
+               {
+                       name:    "initial_connection_overflow",
+                       options: 
[]Option{WithHttp3InitialConnectionReceiveWindow(quicvarint.Max + 1)},
+                       field:   "initial-connection-receive-window",
+               },
+               {
+                       name:    "max_connection_overflow",
+                       options: 
[]Option{WithHttp3MaxConnectionReceiveWindow(quicvarint.Max + 1)},
+                       field:   "max-connection-receive-window",
+               },
+               {
+                       name: "stream_initial_exceeds_maximum",
+                       options: []Option{
+                               WithHttp3InitialStreamReceiveWindow(16 * 1024 * 
1024),
+                               WithHttp3MaxStreamReceiveWindow(1 * 1024 * 
1024),
+                       },
+                       field: "initial-stream-receive-window",
+               },
+               {
+                       name: "connection_initial_exceeds_maximum",
+                       options: []Option{
+                               WithHttp3InitialConnectionReceiveWindow(32 * 
1024 * 1024),
+                               WithHttp3MaxConnectionReceiveWindow(2 * 1024 * 
1024),
+                       },
+                       field: "initial-connection-receive-window",
+               },
+       } {
+               t.Run(test.name, func(t *testing.T) {
+                       opts := NewOptions(test.options...)
+                       _, err := http3config.NewQUICConfig(opts.Triple.Http3, 
nil)
+                       require.Error(t, err)
+                       assert.ErrorContains(t, err, test.field)
+               })
+       }
+
+       opts := NewOptions(
+               WithHttp3InitialStreamReceiveWindow(quicvarint.Max),
+               WithHttp3MaxStreamReceiveWindow(quicvarint.Max),
+               WithHttp3InitialConnectionReceiveWindow(quicvarint.Max),
+               WithHttp3MaxConnectionReceiveWindow(quicvarint.Max),
+       )
+       quicConfig, err := http3config.NewQUICConfig(opts.Triple.Http3, nil)
+       require.NoError(t, err)
+       assert.Equal(t, uint64(quicvarint.Max), 
quicConfig.InitialStreamReceiveWindow)
+       assert.Equal(t, uint64(quicvarint.Max), 
quicConfig.MaxStreamReceiveWindow)
+       assert.Equal(t, uint64(quicvarint.Max), 
quicConfig.InitialConnectionReceiveWindow)
+       assert.Equal(t, uint64(quicvarint.Max), 
quicConfig.MaxConnectionReceiveWindow)
 }
 
 func TestHTTP3DeprecatedAliases(t *testing.T) {

Reply via email to