mirror of
https://github.com/fatedier/frp.git
synced 2025-06-17 09:08:21 +00:00
Compare commits
4 Commits
59c545bb9b
...
231c1dc6bd
Author | SHA1 | Date | |
---|---|---|---|
|
231c1dc6bd | ||
|
acf33db4e4 | ||
|
3585f5c0c0 | ||
|
ced2fb69b7 |
18
Release.md
18
Release.md
@ -1,3 +1,21 @@
|
|||||||
|
### Features
|
||||||
|
|
||||||
|
* Support range ports mapping in TOML/YAML/JSON configuration file by using go template syntax.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
{{- range $_, $v := parseNumberRangePair "6000-6006,6007" "6000-6006,6007" }}
|
||||||
|
[[proxies]]
|
||||||
|
name = "tcp-{{ $v.First }}"
|
||||||
|
type = "tcp"
|
||||||
|
localPort = {{ $v.First }}
|
||||||
|
remotePort = {{ $v.Second }}
|
||||||
|
{{- end }}
|
||||||
|
```
|
||||||
|
|
||||||
|
This will create 8 proxies such as `tcp-6000, tcp-6001, ... tcp-6007`.
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fix the issue of incorrect interval time for rotating the log by day.
|
* Fix the issue of incorrect interval time for rotating the log by day.
|
||||||
|
@ -80,7 +80,10 @@ func DetectLegacyINIFormatFromFile(path string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RenderWithTemplate(in []byte, values *Values) ([]byte, error) {
|
func RenderWithTemplate(in []byte, values *Values) ([]byte, error) {
|
||||||
tmpl, err := template.New("frp").Parse(string(in))
|
tmpl, err := template.New("frp").Funcs(template.FuncMap{
|
||||||
|
"parseNumberRange": parseNumberRange,
|
||||||
|
"parseNumberRangePair": parseNumberRangePair,
|
||||||
|
}).Parse(string(in))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
52
pkg/config/template.go
Normal file
52
pkg/config/template.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2024 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 config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NumberPair struct {
|
||||||
|
First int64
|
||||||
|
Second int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseNumberRangePair(firstRangeStr, secondRangeStr string) ([]NumberPair, error) {
|
||||||
|
firstRangeNumbers, err := util.ParseRangeNumbers(firstRangeStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
secondRangeNumbers, err := util.ParseRangeNumbers(secondRangeStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(firstRangeNumbers) != len(secondRangeNumbers) {
|
||||||
|
return nil, fmt.Errorf("first and second range numbers are not in pairs")
|
||||||
|
}
|
||||||
|
pairs := make([]NumberPair, 0, len(firstRangeNumbers))
|
||||||
|
for i := 0; i < len(firstRangeNumbers); i++ {
|
||||||
|
pairs = append(pairs, NumberPair{
|
||||||
|
First: firstRangeNumbers[i],
|
||||||
|
Second: secondRangeNumbers[i],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return pairs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseNumberRange(firstRangeStr string) ([]int64, error) {
|
||||||
|
return util.ParseRangeNumbers(firstRangeStr)
|
||||||
|
}
|
@ -17,7 +17,7 @@ package nathole
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand/v2"
|
||||||
"net"
|
"net"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -341,7 +341,7 @@ func sendSidMessage(
|
|||||||
TransactionID: transactionID,
|
TransactionID: transactionID,
|
||||||
Sid: sid,
|
Sid: sid,
|
||||||
Response: false,
|
Response: false,
|
||||||
Nonce: strings.Repeat("0", rand.Intn(20)),
|
Nonce: strings.Repeat("0", rand.IntN(20)),
|
||||||
}
|
}
|
||||||
buf, err := EncodeMessage(m, key)
|
buf, err := EncodeMessage(m, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -398,7 +398,7 @@ func sendSidMessageToRandomPorts(
|
|||||||
used := sets.New[int]()
|
used := sets.New[int]()
|
||||||
getUnusedPort := func() int {
|
getUnusedPort := func() int {
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
port := rand.Intn(65535-1024) + 1024
|
port := rand.IntN(65535-1024) + 1024
|
||||||
if !used.Has(port) {
|
if !used.Has(port) {
|
||||||
used.Insert(port)
|
used.Insert(port)
|
||||||
return port
|
return port
|
||||||
|
@ -20,7 +20,7 @@ import (
|
|||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
mathrand "math/rand"
|
mathrand "math/rand/v2"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -124,7 +124,7 @@ func RandomSleep(duration time.Duration, minRatio, maxRatio float64) time.Durati
|
|||||||
if max <= min {
|
if max <= min {
|
||||||
n = min
|
n = min
|
||||||
} else {
|
} else {
|
||||||
n = mathrand.Int63n(max-min) + min
|
n = mathrand.Int64N(max-min) + min
|
||||||
}
|
}
|
||||||
d := duration * time.Duration(n) / time.Duration(1000)
|
d := duration * time.Duration(n) / time.Duration(1000)
|
||||||
time.Sleep(d)
|
time.Sleep(d)
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
package version
|
package version
|
||||||
|
|
||||||
var version = "0.55.1"
|
var version = "0.56.0"
|
||||||
|
|
||||||
func Full() string {
|
func Full() string {
|
||||||
return version
|
return version
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
package wait
|
package wait
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand/v2"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/util/util"
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package framework
|
package framework
|
||||||
|
|
||||||
type FRPClient struct {
|
import (
|
||||||
port int
|
clientsdk "github.com/fatedier/frp/pkg/sdk/client"
|
||||||
}
|
)
|
||||||
|
|
||||||
func (f *Framework) FRPClient(port int) *FRPClient {
|
func (f *Framework) APIClientForFrpc(port int) *clientsdk.Client {
|
||||||
return &FRPClient{
|
return clientsdk.New("127.0.0.1", port)
|
||||||
port: port,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,10 @@ func ExpectNoErrorWithOffset(offset int, err error, explain ...interface{}) {
|
|||||||
gomega.ExpectWithOffset(1+offset, err).NotTo(gomega.HaveOccurred(), explain...)
|
gomega.ExpectWithOffset(1+offset, err).NotTo(gomega.HaveOccurred(), explain...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExpectContainSubstring(actual, substr string, explain ...interface{}) {
|
||||||
|
gomega.ExpectWithOffset(1, actual).To(gomega.ContainSubstring(substr), explain...)
|
||||||
|
}
|
||||||
|
|
||||||
// ExpectConsistOf expects actual contains precisely the extra elements. The ordering of the elements does not matter.
|
// ExpectConsistOf expects actual contains precisely the extra elements. The ordering of the elements does not matter.
|
||||||
func ExpectConsistOf(actual interface{}, extra interface{}, explain ...interface{}) {
|
func ExpectConsistOf(actual interface{}, extra interface{}, explain ...interface{}) {
|
||||||
gomega.ExpectWithOffset(1, actual).To(gomega.ConsistOf(extra), explain...)
|
gomega.ExpectWithOffset(1, actual).To(gomega.ConsistOf(extra), explain...)
|
||||||
|
@ -217,7 +217,7 @@ func (f *Framework) RenderTemplates(templates []string) (outs []string, ports ma
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, t := range templates {
|
for _, t := range templates {
|
||||||
tmpl, err := template.New("").Parse(t)
|
tmpl, err := template.New("frp-e2e").Parse(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
"github.com/onsi/ginkgo/v2"
|
"github.com/onsi/ginkgo/v2"
|
||||||
|
|
||||||
clientsdk "github.com/fatedier/frp/pkg/sdk/client"
|
|
||||||
"github.com/fatedier/frp/test/e2e/framework"
|
"github.com/fatedier/frp/test/e2e/framework"
|
||||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||||
"github.com/fatedier/frp/test/e2e/pkg/request"
|
"github.com/fatedier/frp/test/e2e/pkg/request"
|
||||||
@ -54,7 +53,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
|
|||||||
framework.NewRequestExpect(f).Port(p2Port).Ensure()
|
framework.NewRequestExpect(f).Port(p2Port).Ensure()
|
||||||
framework.NewRequestExpect(f).Port(p3Port).Ensure()
|
framework.NewRequestExpect(f).Port(p3Port).Ensure()
|
||||||
|
|
||||||
client := clientsdk.New("127.0.0.1", adminPort)
|
client := f.APIClientForFrpc(adminPort)
|
||||||
conf, err := client.GetConfig()
|
conf, err := client.GetConfig()
|
||||||
framework.ExpectNoError(err)
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
@ -120,7 +119,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
|
|||||||
|
|
||||||
framework.NewRequestExpect(f).Port(testPort).Ensure()
|
framework.NewRequestExpect(f).Port(testPort).Ensure()
|
||||||
|
|
||||||
client := clientsdk.New("127.0.0.1", adminPort)
|
client := f.APIClientForFrpc(adminPort)
|
||||||
err := client.Stop()
|
err := client.Stop()
|
||||||
framework.ExpectNoError(err)
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"github.com/onsi/ginkgo/v2"
|
"github.com/onsi/ginkgo/v2"
|
||||||
|
|
||||||
clientsdk "github.com/fatedier/frp/pkg/sdk/client"
|
|
||||||
"github.com/fatedier/frp/test/e2e/framework"
|
"github.com/fatedier/frp/test/e2e/framework"
|
||||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||||
"github.com/fatedier/frp/test/e2e/pkg/port"
|
"github.com/fatedier/frp/test/e2e/pkg/port"
|
||||||
@ -99,7 +98,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
|
|||||||
|
|
||||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||||
|
|
||||||
client := clientsdk.New("127.0.0.1", adminPort)
|
client := f.APIClientForFrpc(adminPort)
|
||||||
|
|
||||||
// tcp random port
|
// tcp random port
|
||||||
status, err := client.GetProxyStatus("tcp")
|
status, err := client.GetProxyStatus("tcp")
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
"github.com/onsi/ginkgo/v2"
|
"github.com/onsi/ginkgo/v2"
|
||||||
|
|
||||||
clientsdk "github.com/fatedier/frp/pkg/sdk/client"
|
|
||||||
"github.com/fatedier/frp/test/e2e/framework"
|
"github.com/fatedier/frp/test/e2e/framework"
|
||||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||||
"github.com/fatedier/frp/test/e2e/pkg/request"
|
"github.com/fatedier/frp/test/e2e/pkg/request"
|
||||||
@ -57,7 +56,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
|
|||||||
framework.NewRequestExpect(f).Port(p2Port).Ensure()
|
framework.NewRequestExpect(f).Port(p2Port).Ensure()
|
||||||
framework.NewRequestExpect(f).Port(p3Port).Ensure()
|
framework.NewRequestExpect(f).Port(p3Port).Ensure()
|
||||||
|
|
||||||
client := clientsdk.New("127.0.0.1", adminPort)
|
client := f.APIClientForFrpc(adminPort)
|
||||||
conf, err := client.GetConfig()
|
conf, err := client.GetConfig()
|
||||||
framework.ExpectNoError(err)
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
@ -124,7 +123,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
|
|||||||
|
|
||||||
framework.NewRequestExpect(f).Port(testPort).Ensure()
|
framework.NewRequestExpect(f).Port(testPort).Ensure()
|
||||||
|
|
||||||
client := clientsdk.New("127.0.0.1", adminPort)
|
client := f.APIClientForFrpc(adminPort)
|
||||||
err := client.Stop()
|
err := client.Stop()
|
||||||
framework.ExpectNoError(err)
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
|
@ -38,6 +38,51 @@ var _ = ginkgo.Describe("[Feature: Config]", func() {
|
|||||||
|
|
||||||
framework.NewRequestExpect(f).PortName(portName).Ensure()
|
framework.NewRequestExpect(f).PortName(portName).Ensure()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ginkgo.It("Range ports mapping", func() {
|
||||||
|
serverConf := consts.DefaultServerConfig
|
||||||
|
clientConf := consts.DefaultClientConfig
|
||||||
|
|
||||||
|
adminPort := f.AllocPort()
|
||||||
|
|
||||||
|
localPortsRange := "13010-13012,13014"
|
||||||
|
remotePortsRange := "23010-23012,23014"
|
||||||
|
escapeTemplate := func(s string) string {
|
||||||
|
return "{{ `" + s + "` }}"
|
||||||
|
}
|
||||||
|
clientConf += fmt.Sprintf(`
|
||||||
|
webServer.port = %d
|
||||||
|
|
||||||
|
%s
|
||||||
|
[[proxies]]
|
||||||
|
name = "tcp-%s"
|
||||||
|
type = "tcp"
|
||||||
|
localPort = %s
|
||||||
|
remotePort = %s
|
||||||
|
%s
|
||||||
|
`, adminPort,
|
||||||
|
escapeTemplate(fmt.Sprintf(`{{- range $_, $v := parseNumberRangePair "%s" "%s" }}`, localPortsRange, remotePortsRange)),
|
||||||
|
escapeTemplate("{{ $v.First }}"),
|
||||||
|
escapeTemplate("{{ $v.First }}"),
|
||||||
|
escapeTemplate("{{ $v.Second }}"),
|
||||||
|
escapeTemplate("{{- end }}"),
|
||||||
|
)
|
||||||
|
|
||||||
|
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||||
|
|
||||||
|
client := f.APIClientForFrpc(adminPort)
|
||||||
|
checkProxyFn := func(name string, localPort, remotePort int) {
|
||||||
|
status, err := client.GetProxyStatus(name)
|
||||||
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
|
framework.ExpectContainSubstring(status.LocalAddr, fmt.Sprintf(":%d", localPort))
|
||||||
|
framework.ExpectContainSubstring(status.RemoteAddr, fmt.Sprintf(":%d", remotePort))
|
||||||
|
}
|
||||||
|
checkProxyFn("tcp-13010", 13010, 23010)
|
||||||
|
checkProxyFn("tcp-13011", 13011, 23011)
|
||||||
|
checkProxyFn("tcp-13012", 13012, 23012)
|
||||||
|
checkProxyFn("tcp-13014", 13014, 23014)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
ginkgo.Describe("Includes", func() {
|
ginkgo.Describe("Includes", func() {
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"github.com/onsi/ginkgo/v2"
|
"github.com/onsi/ginkgo/v2"
|
||||||
|
|
||||||
clientsdk "github.com/fatedier/frp/pkg/sdk/client"
|
|
||||||
"github.com/fatedier/frp/test/e2e/framework"
|
"github.com/fatedier/frp/test/e2e/framework"
|
||||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||||
"github.com/fatedier/frp/test/e2e/pkg/port"
|
"github.com/fatedier/frp/test/e2e/pkg/port"
|
||||||
@ -110,7 +109,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
|
|||||||
|
|
||||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||||
|
|
||||||
client := clientsdk.New("127.0.0.1", adminPort)
|
client := f.APIClientForFrpc(adminPort)
|
||||||
|
|
||||||
// tcp random port
|
// tcp random port
|
||||||
status, err := client.GetProxyStatus("tcp")
|
status, err := client.GetProxyStatus("tcp")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user