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 d4ce45338 perf(triple): add fast paths for common content-type 
canonicalization (#3416)
d4ce45338 is described below

commit d4ce453382a0e6ea18c9840e3a69ac8264790f60
Author: Harsh Mehta <[email protected]>
AuthorDate: Sun Jun 14 18:57:52 2026 +0530

    perf(triple): add fast paths for common content-type canonicalization 
(#3416)
    
    * perf(triple): add fast paths for common content-type canonicalization
    
    Signed-off-by: Harsh Mehta <[email protected]>
    
    * perf(protocol): implement fast path for content-type canonicalization
    
    Signed-off-by: Harsh Mehta <[email protected]>
    
    ---------
    
    Signed-off-by: Harsh Mehta <[email protected]>
---
 protocol/triple/triple_protocol/protocol.go      | 110 +++++++++++++++++++----
 protocol/triple/triple_protocol/protocol_test.go |  26 ++++--
 2 files changed, 112 insertions(+), 24 deletions(-)

diff --git a/protocol/triple/triple_protocol/protocol.go 
b/protocol/triple/triple_protocol/protocol.go
index a6f7aea0d..d8e872b99 100644
--- a/protocol/triple/triple_protocol/protocol.go
+++ b/protocol/triple/triple_protocol/protocol.go
@@ -329,27 +329,49 @@ func flushResponseWriter(w http.ResponseWriter) {
 }
 
 func canonicalizeContentType(contentType string) string {
-       // Typically, clients send Content-Type in canonical form, without
-       // parameters. In those cases, we'd like to avoid parsing and
-       // canonicalization overhead.
-       //
-       // See https://www.rfc-editor.org/rfc/rfc2045.html#section-5.1 for a 
full
-       // grammar.
-       var slashes int
-       for _, r := range contentType {
-               switch {
-               case r >= 'a' && r <= 'z':
-               case r == '.' || r == '+' || r == '-':
-               case r == '/':
-                       slashes++
-               default:
-                       return canonicalizeContentTypeSlow(contentType)
+       if canonical, ok := canonicalizeContentTypeFast(contentType); ok {
+               return canonical
+       }
+       return canonicalizeContentTypeSlow(contentType)
+}
+
+// canonicalizeContentTypeFast handles parameter-free types and the common
+// "; charset=<token>" case without invoking mime.ParseMediaType.
+// See https://www.rfc-editor.org/rfc/rfc2045.html#section-5.1 for a full 
grammar.
+func canonicalizeContentTypeFast(contentType string) (string, bool) {
+       semi := strings.IndexByte(contentType, ';')
+       if semi < 0 {
+               if isLowercaseMediaType(contentType) {
+                       return contentType, true
                }
+               return "", false
        }
-       if slashes == 1 {
-               return contentType
+
+       base := contentType[:semi]
+       param := contentType[semi+1:]
+
+       if !isLowercaseMediaType(base) {
+               return "", false
        }
-       return canonicalizeContentTypeSlow(contentType)
+       if strings.Contains(param, ";") {
+               return "", false
+       }
+
+       const prefix = " charset="
+       if !strings.HasPrefix(param, prefix) {
+               return "", false
+       }
+
+       charset := param[len(prefix):]
+       if !isSimpleCharsetToken(charset) {
+               return "", false
+       }
+
+       lower := strings.ToLower(charset)
+       if lower == charset {
+               return contentType, true
+       }
+       return base + "; charset=" + lower, true
 }
 
 func canonicalizeContentTypeSlow(contentType string) string {
@@ -367,3 +389,55 @@ func canonicalizeContentTypeSlow(contentType string) 
string {
        }
        return mime.FormatMediaType(base, params)
 }
+
+// isLowercaseMediaType reports whether s is a well-formed, already-lowercase
+// media type: both type and subtype non-empty, separated by exactly one '/'.
+func isLowercaseMediaType(s string) bool {
+       slash := strings.IndexByte(s, '/')
+       if slash <= 0 || slash == len(s)-1 {
+               return false
+       }
+       if strings.IndexByte(s[slash+1:], '/') >= 0 {
+               return false
+       }
+       return isLowercaseToken(s[:slash]) && isLowercaseToken(s[slash+1:])
+}
+
+// isLowercaseToken reports whether s is a non-empty token of lowercase 
letters,
+// digits, '.', '+', and '-'.
+func isLowercaseToken(s string) bool {
+       if s == "" {
+               return false
+       }
+       for i := 0; i < len(s); i++ {
+               c := s[i]
+               switch {
+               case c >= 'a' && c <= 'z':
+               case c >= '0' && c <= '9':
+               case c == '.' || c == '+' || c == '-':
+               default:
+                       return false
+               }
+       }
+       return true
+}
+
+// isSimpleCharsetToken reports whether s is a non-empty token composed of
+// letters, digits, '-', '_', and '.'.
+func isSimpleCharsetToken(s string) bool {
+       if s == "" {
+               return false
+       }
+       for i := 0; i < len(s); i++ {
+               c := s[i]
+               switch {
+               case c >= 'a' && c <= 'z':
+               case c >= 'A' && c <= 'Z':
+               case c >= '0' && c <= '9':
+               case c == '-' || c == '_' || c == '.':
+               default:
+                       return false
+               }
+       }
+       return true
+}
diff --git a/protocol/triple/triple_protocol/protocol_test.go 
b/protocol/triple/triple_protocol/protocol_test.go
index ffbc09ad2..9ffd3a428 100644
--- a/protocol/triple/triple_protocol/protocol_test.go
+++ b/protocol/triple/triple_protocol/protocol_test.go
@@ -29,10 +29,17 @@ func TestCanonicalizeContentType(t *testing.T) {
                arg  string
                want string
        }{
+               // slow path: base type needs case normalization
                {name: "uppercase should be normalized", arg: 
"APPLICATION/json", want: "application/json"},
-               {name: "charset param should be treated as lowercase", arg: 
"application/json; charset=UTF-8", want: "application/json; charset=utf-8"},
-               {name: "non charset param should not be changed", arg: 
"multipart/form-data; boundary=fooBar", want: "multipart/form-data; 
boundary=fooBar"},
                {name: "no parameters should be normalized", arg: 
"APPLICATION/json;  ", want: "application/json"},
+               // slow path: non-charset parameter
+               {name: "non charset param should not be changed", arg: 
"multipart/form-data; boundary=fooBar", want: "multipart/form-data; 
boundary=fooBar"},
+               // fast path: charset parameter normalization
+               {name: "charset param uppercase normalized via fast path", arg: 
"application/json; charset=UTF-8", want: "application/json; charset=utf-8"},
+               {name: "charset param already lowercase returned as-is", arg: 
"application/json; charset=utf-8", want: "application/json; charset=utf-8"},
+               // malformed: must fall through to slow path unchanged (not 
rewritten by fast path)
+               {name: "malformed missing type", arg: "/json; charset=UTF-8", 
want: "/json; charset=UTF-8"},
+               {name: "malformed missing subtype", arg: "application/; 
charset=UTF-8", want: "application/; charset=UTF-8"},
        }
        for _, tt := range tests {
                tt := tt
@@ -45,23 +52,30 @@ func TestCanonicalizeContentType(t *testing.T) {
 
 func BenchmarkCanonicalizeContentType(b *testing.B) {
        b.Run("simple", func(b *testing.B) {
+               b.ReportAllocs()
                for i := 0; i < b.N; i++ {
                        _ = canonicalizeContentType("application/json")
                }
-               b.ReportAllocs()
        })
 
-       b.Run("with charset", func(b *testing.B) {
+       b.Run("charset canonical", func(b *testing.B) {
+               b.ReportAllocs()
                for i := 0; i < b.N; i++ {
                        _ = canonicalizeContentType("application/json; 
charset=utf-8")
                }
+       })
+
+       b.Run("charset uppercase", func(b *testing.B) {
                b.ReportAllocs()
+               for i := 0; i < b.N; i++ {
+                       _ = canonicalizeContentType("application/json; 
charset=UTF-8")
+               }
        })
 
-       b.Run("with other param", func(b *testing.B) {
+       b.Run("non-charset param", func(b *testing.B) {
+               b.ReportAllocs()
                for i := 0; i < b.N; i++ {
                        _ = canonicalizeContentType("application/json; 
foo=utf-8")
                }
-               b.ReportAllocs()
        })
 }

Reply via email to