diff --git a/ctx.go b/ctx.go index 7e97ccd3b95..244df758942 100644 --- a/ctx.go +++ b/ctx.go @@ -662,10 +662,38 @@ func (c *Ctx) IP() string { // extractValidIPs will return a slice of strings that represent valid IP addresses // in the input string. The order is maintained. The separator is a comma func extractValidIPs(input string) (validIPs []string) { - unvalidatedIps := strings.Split(input, ",") - for _, ip := range unvalidatedIps { - if parsedIp := net.ParseIP(strings.TrimSpace(ip)); parsedIp != nil { - validIPs = append(validIPs, parsedIp.String()) + + // try to gather IPs in the input with minimal allocations to improve performance + ips := make([]string, bytes.Count([]byte(input), []byte(","))+1) + var commaPos, i, validCount int + for { + commaPos = bytes.IndexByte([]byte(input), ',') + if commaPos != -1 { + if net.ParseIP(utils.Trim(input[:commaPos], ' ')) != nil { + ips[i] = utils.Trim(input[:commaPos], ' ') + validCount++ + } + input, i = input[commaPos+1:], i+1 + } else { + if net.ParseIP(utils.Trim(input, ' ')) != nil { + ips[i] = utils.Trim(input, ' ') + validCount++ + } + break + } + } + + // filter out any bad IPs that we found + if len(ips) == validCount { + validIPs = ips + } else { + validIPs = make([]string, validCount) + var validIndex int + for n := range ips { + if ips[n] != "" { + validIPs[validIndex] = ips[n] + validIndex++ + } } } return diff --git a/ctx_test.go b/ctx_test.go index 98905b42490..e166dcaebfa 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -1215,6 +1215,34 @@ func Benchmark_Ctx_IPs(b *testing.B) { utils.AssertEqual(b, []string{"127.0.0.1", "127.0.0.1", "127.0.0.1"}, res) } +func Benchmark_Ctx_IP_With_ProxyHeader(b *testing.B) { + app := New(Config{ProxyHeader: HeaderXForwardedFor}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1") + var res string + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + res = c.IP() + } + utils.AssertEqual(b, "127.0.0.1", res) +} + +func Benchmark_Ctx_IP(b *testing.B) { + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.Request() + var res string + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + res = c.IP() + } + utils.AssertEqual(b, "0.0.0.0", res) +} + // go test -run Test_Ctx_Is func Test_Ctx_Is(t *testing.T) { t.Parallel()