mirror of
https://github.com/fatedier/frp.git
synced 2026-08-01 03:12:54 +08:00
200 lines
5.7 KiB
Go
200 lines
5.7 KiB
Go
// Copyright 2026 The frp Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package msg
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"net"
|
|
"runtime"
|
|
"testing"
|
|
|
|
"github.com/fatedier/frp/pkg/proto/wire"
|
|
)
|
|
|
|
type udpBenchmarkCase struct {
|
|
name string
|
|
packet *UDPPacket
|
|
}
|
|
|
|
var (
|
|
udpBenchmarkBytesSink []byte
|
|
udpBenchmarkMessageSink Message
|
|
)
|
|
|
|
func udpBenchmarkCases(payloadSize int) []udpBenchmarkCase {
|
|
content := bytes.Repeat([]byte{0x5a}, payloadSize)
|
|
return []udpBenchmarkCase{
|
|
{
|
|
name: "ipv4-remote",
|
|
packet: &UDPPacket{
|
|
Content: content,
|
|
RemoteAddr: &net.UDPAddr{IP: net.ParseIP("192.0.2.1"), Port: 12345},
|
|
},
|
|
},
|
|
{
|
|
name: "ipv4-local-remote",
|
|
packet: &UDPPacket{
|
|
Content: content,
|
|
LocalAddr: &net.UDPAddr{IP: net.ParseIP("192.0.2.2"), Port: 23456},
|
|
RemoteAddr: &net.UDPAddr{IP: net.ParseIP("192.0.2.1"), Port: 12345},
|
|
},
|
|
},
|
|
{
|
|
name: "ipv6-remote",
|
|
packet: &UDPPacket{
|
|
Content: content,
|
|
RemoteAddr: &net.UDPAddr{IP: net.ParseIP("2001:db8::1"), Port: 12345},
|
|
},
|
|
},
|
|
{
|
|
name: "ipv6-local-remote",
|
|
packet: &UDPPacket{
|
|
Content: content,
|
|
LocalAddr: &net.UDPAddr{IP: net.ParseIP("2001:db8::2"), Port: 23456, Zone: "bench0"},
|
|
RemoteAddr: &net.UDPAddr{IP: net.ParseIP("2001:db8::1"), Port: 12345, Zone: "bench1"},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestUDPPacketV2FrameSizes(t *testing.T) {
|
|
t.Logf("environment go=%s goos=%s goarch=%s gomaxprocs=%d", runtime.Version(), runtime.GOOS, runtime.GOARCH, runtime.GOMAXPROCS(0))
|
|
for _, payloadSize := range []int{64, 512, 1200, 1472} {
|
|
for _, tc := range udpBenchmarkCases(payloadSize) {
|
|
jsonFrame := udpBenchmarkWireBytes(t, tc.packet, "")
|
|
binaryFrame := udpBenchmarkWireBytes(t, tc.packet, wire.UDPPacketCodecBinary)
|
|
saving := 100 * float64(len(jsonFrame)-len(binaryFrame)) / float64(len(jsonFrame))
|
|
t.Logf("frame payload=%d case=%s json_bytes=%d binary_bytes=%d binary_saving_pct=%.2f", payloadSize, tc.name, len(jsonFrame), len(binaryFrame), saving)
|
|
}
|
|
}
|
|
}
|
|
|
|
func udpBenchmarkWireBytes(t testing.TB, packet *UDPPacket, codec string) []byte {
|
|
t.Helper()
|
|
var buf bytes.Buffer
|
|
rw, err := NewUDPPacketReadWriter(&buf, wire.ProtocolV2, codec)
|
|
if err != nil {
|
|
t.Fatalf("create UDP read writer: %v", err)
|
|
}
|
|
if err := rw.WriteMsg(packet); err != nil {
|
|
t.Fatalf("write UDP packet: %v", err)
|
|
}
|
|
return append([]byte(nil), buf.Bytes()...)
|
|
}
|
|
|
|
type udpBenchmarkReadWriter struct {
|
|
reader bytes.Reader
|
|
}
|
|
|
|
func (rw *udpBenchmarkReadWriter) Read(p []byte) (int, error) {
|
|
return rw.reader.Read(p)
|
|
}
|
|
|
|
func (rw *udpBenchmarkReadWriter) Write(p []byte) (int, error) {
|
|
return len(p), nil
|
|
}
|
|
|
|
func (rw *udpBenchmarkReadWriter) Reset(p []byte) {
|
|
rw.reader.Reset(p)
|
|
}
|
|
|
|
func udpBenchmarkValidatePacket(b testing.TB, got, want *UDPPacket) {
|
|
b.Helper()
|
|
if !bytes.Equal(got.Content, want.Content) || !udpBenchmarkUDPAddrEqual(got.LocalAddr, want.LocalAddr) ||
|
|
!udpBenchmarkUDPAddrEqual(got.RemoteAddr, want.RemoteAddr) {
|
|
b.Fatalf("decoded packet mismatch: got %+v, want %+v", got, want)
|
|
}
|
|
}
|
|
|
|
func udpBenchmarkUDPAddrEqual(got, want *net.UDPAddr) bool {
|
|
if got == nil || want == nil {
|
|
return got == want
|
|
}
|
|
return got.IP.Equal(want.IP) && got.Port == want.Port && got.Zone == want.Zone
|
|
}
|
|
|
|
func BenchmarkUDPPacketV2CodecWrite(b *testing.B) {
|
|
for _, payloadSize := range []int{64, 512, 1200, 1472} {
|
|
for _, tc := range udpBenchmarkCases(payloadSize) {
|
|
for _, codec := range []struct {
|
|
name string
|
|
value string
|
|
}{
|
|
{name: "json", value: ""},
|
|
{name: "binary", value: wire.UDPPacketCodecBinary},
|
|
} {
|
|
b.Run(fmt.Sprintf("payload-%d/%s/%s", payloadSize, tc.name, codec.name), func(b *testing.B) {
|
|
var buf bytes.Buffer
|
|
rw, err := NewUDPPacketReadWriter(&buf, wire.ProtocolV2, codec.value)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
expected := udpBenchmarkWireBytes(b, tc.packet, codec.value)
|
|
b.SetBytes(int64(len(expected)))
|
|
for b.Loop() {
|
|
buf.Reset()
|
|
if err := rw.WriteMsg(tc.packet); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
if !bytes.Equal(buf.Bytes(), expected) {
|
|
b.Fatalf("encoded packet mismatch: got %d bytes, want %d", buf.Len(), len(expected))
|
|
}
|
|
udpBenchmarkBytesSink = buf.Bytes()
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkUDPPacketV2CodecRead(b *testing.B) {
|
|
for _, payloadSize := range []int{64, 512, 1200, 1472} {
|
|
for _, tc := range udpBenchmarkCases(payloadSize) {
|
|
for _, codec := range []struct {
|
|
name string
|
|
value string
|
|
}{
|
|
{name: "json", value: ""},
|
|
{name: "binary", value: wire.UDPPacketCodecBinary},
|
|
} {
|
|
b.Run(fmt.Sprintf("payload-%d/%s/%s", payloadSize, tc.name, codec.name), func(b *testing.B) {
|
|
encoded := udpBenchmarkWireBytes(b, tc.packet, codec.value)
|
|
stream := &udpBenchmarkReadWriter{}
|
|
rw, err := NewUDPPacketReadWriter(stream, wire.ProtocolV2, codec.value)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
var decoded Message
|
|
b.SetBytes(int64(len(encoded)))
|
|
for b.Loop() {
|
|
stream.Reset(encoded)
|
|
decoded, err = rw.ReadMsg()
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
packet, ok := decoded.(*UDPPacket)
|
|
if !ok {
|
|
b.Fatalf("decoded message type %T, want *UDPPacket", decoded)
|
|
}
|
|
udpBenchmarkValidatePacket(b, packet, tc.packet)
|
|
udpBenchmarkMessageSink = decoded
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|