mirror of
https://github.com/fatedier/frp.git
synced 2026-04-28 03:49:09 +08:00
protocol: add v2 wire protocol with binary framing and capability negotiation (#5294)
This commit is contained in:
@@ -53,6 +53,8 @@ type Server struct {
|
||||
tokenRequestCount atomic.Int64
|
||||
}
|
||||
|
||||
const maxTokenRequestBodySize = 1 << 20
|
||||
|
||||
type Option func(*Server)
|
||||
|
||||
func WithBindPort(port int) Option {
|
||||
@@ -178,6 +180,7 @@ func (s *Server) handleToken(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxTokenRequestBodySize)
|
||||
if err := r.ParseForm(); err != nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
@@ -187,7 +190,7 @@ func (s *Server) handleToken(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if r.FormValue("grant_type") != "client_credentials" {
|
||||
if r.Form.Get("grant_type") != "client_credentials" {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
@@ -199,8 +202,8 @@ func (s *Server) handleToken(w http.ResponseWriter, r *http.Request) {
|
||||
// Accept credentials from Basic Auth or form body.
|
||||
clientID, clientSecret, ok := r.BasicAuth()
|
||||
if !ok {
|
||||
clientID = r.FormValue("client_id")
|
||||
clientSecret = r.FormValue("client_secret")
|
||||
clientID = r.Form.Get("client_id")
|
||||
clientSecret = r.Form.Get("client_secret")
|
||||
}
|
||||
if clientID != s.clientID || clientSecret != s.clientSecret {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
163
test/e2e/v1/basic/wire.go
Normal file
163
test/e2e/v1/basic/wire.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package basic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/onsi/ginkgo/v2"
|
||||
|
||||
"github.com/fatedier/frp/test/e2e/framework"
|
||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/port"
|
||||
)
|
||||
|
||||
var _ = ginkgo.Describe("[Feature: WireProtocol]", func() {
|
||||
f := framework.NewDefaultFramework()
|
||||
|
||||
ginkgo.It("v1 tcp and udp proxies", func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
tcpPortName := port.GenName("WireV1TCP")
|
||||
udpPortName := port.GenName("WireV1UDP")
|
||||
clientConf := consts.DefaultClientConfig + fmt.Sprintf(`
|
||||
transport.wireProtocol = "v1"
|
||||
|
||||
[[proxies]]
|
||||
name = "tcp"
|
||||
type = "tcp"
|
||||
localPort = {{ .%s }}
|
||||
remotePort = {{ .%s }}
|
||||
|
||||
[[proxies]]
|
||||
name = "udp"
|
||||
type = "udp"
|
||||
localPort = {{ .%s }}
|
||||
remotePort = {{ .%s }}
|
||||
`, framework.TCPEchoServerPort, tcpPortName, framework.UDPEchoServerPort, udpPortName)
|
||||
|
||||
f.RunProcesses(serverConf, []string{clientConf})
|
||||
|
||||
framework.NewRequestExpect(f).PortName(tcpPortName).Ensure()
|
||||
framework.NewRequestExpect(f).Protocol("udp").PortName(udpPortName).Ensure()
|
||||
})
|
||||
|
||||
ginkgo.It("v2 tcp and udp proxies", func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
tcpPortName := port.GenName("WireV2TCP")
|
||||
udpPortName := port.GenName("WireV2UDP")
|
||||
clientConf := consts.DefaultClientConfig + fmt.Sprintf(`
|
||||
transport.wireProtocol = "v2"
|
||||
|
||||
[[proxies]]
|
||||
name = "tcp"
|
||||
type = "tcp"
|
||||
localPort = {{ .%s }}
|
||||
remotePort = {{ .%s }}
|
||||
|
||||
[[proxies]]
|
||||
name = "udp"
|
||||
type = "udp"
|
||||
localPort = {{ .%s }}
|
||||
remotePort = {{ .%s }}
|
||||
`, framework.TCPEchoServerPort, tcpPortName, framework.UDPEchoServerPort, udpPortName)
|
||||
|
||||
f.RunProcesses(serverConf, []string{clientConf})
|
||||
|
||||
framework.NewRequestExpect(f).PortName(tcpPortName).Ensure()
|
||||
framework.NewRequestExpect(f).Protocol("udp").PortName(udpPortName).Ensure()
|
||||
})
|
||||
|
||||
ginkgo.It("v2 stcp visitor", func() {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
bindPortName := port.GenName("WireV2STCP")
|
||||
clientServerConf := consts.DefaultClientConfig + fmt.Sprintf(`
|
||||
user = "user1"
|
||||
transport.wireProtocol = "v2"
|
||||
|
||||
[[proxies]]
|
||||
name = "stcp"
|
||||
type = "stcp"
|
||||
secretKey = "abc"
|
||||
localPort = {{ .%s }}
|
||||
`, framework.TCPEchoServerPort)
|
||||
clientVisitorConf := consts.DefaultClientConfig + fmt.Sprintf(`
|
||||
user = "user1"
|
||||
transport.wireProtocol = "v2"
|
||||
|
||||
[[visitors]]
|
||||
name = "stcp-visitor"
|
||||
type = "stcp"
|
||||
serverName = "stcp"
|
||||
secretKey = "abc"
|
||||
bindPort = {{ .%s }}
|
||||
`, bindPortName)
|
||||
|
||||
f.RunProcesses(serverConf, []string{clientServerConf, clientVisitorConf})
|
||||
|
||||
framework.NewRequestExpect(f).PortName(bindPortName).Ensure()
|
||||
})
|
||||
|
||||
ginkgo.It("reports client wire protocol", func() {
|
||||
webPort := f.AllocPort()
|
||||
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
||||
webServer.port = %d
|
||||
`, webPort)
|
||||
|
||||
v1PortName := port.GenName("WireReportV1")
|
||||
v1ClientConf := consts.DefaultClientConfig + fmt.Sprintf(`
|
||||
clientID = "wire-v1"
|
||||
transport.wireProtocol = "v1"
|
||||
|
||||
[[proxies]]
|
||||
name = "v1"
|
||||
type = "tcp"
|
||||
localPort = {{ .%s }}
|
||||
remotePort = {{ .%s }}
|
||||
`, framework.TCPEchoServerPort, v1PortName)
|
||||
|
||||
v2PortName := port.GenName("WireReportV2")
|
||||
v2ClientConf := consts.DefaultClientConfig + fmt.Sprintf(`
|
||||
clientID = "wire-v2"
|
||||
transport.wireProtocol = "v2"
|
||||
|
||||
[[proxies]]
|
||||
name = "v2"
|
||||
type = "tcp"
|
||||
localPort = {{ .%s }}
|
||||
remotePort = {{ .%s }}
|
||||
`, framework.TCPEchoServerPort, v2PortName)
|
||||
|
||||
f.RunProcesses(serverConf, []string{v1ClientConf, v2ClientConf})
|
||||
|
||||
framework.NewRequestExpect(f).PortName(v1PortName).Ensure()
|
||||
framework.NewRequestExpect(f).PortName(v2PortName).Ensure()
|
||||
expectClientWireProtocol(webPort, "wire-v1", "v1")
|
||||
expectClientWireProtocol(webPort, "wire-v2", "v2")
|
||||
})
|
||||
})
|
||||
|
||||
type wireClientInfo struct {
|
||||
ClientID string `json:"clientID"`
|
||||
WireProtocol string `json:"wireProtocol"`
|
||||
}
|
||||
|
||||
func expectClientWireProtocol(webPort int, clientID string, wireProtocol string) {
|
||||
resp, err := http.Get(fmt.Sprintf("http://127.0.0.1:%d/api/clients", webPort))
|
||||
framework.ExpectNoError(err)
|
||||
defer resp.Body.Close()
|
||||
framework.ExpectEqual(resp.StatusCode, 200)
|
||||
|
||||
content, err := io.ReadAll(resp.Body)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
var clients []wireClientInfo
|
||||
framework.ExpectNoError(json.Unmarshal(content, &clients))
|
||||
for _, client := range clients {
|
||||
if client.ClientID == clientID {
|
||||
framework.ExpectEqual(client.WireProtocol, wireProtocol)
|
||||
return
|
||||
}
|
||||
}
|
||||
framework.Failf("client %q not found in /api/clients response: %s", clientID, string(content))
|
||||
}
|
||||
Reference in New Issue
Block a user