Merge 541b0eb5787065696126069a9ca56e641cbc2d55 into dcaa4296d111981ffb31ac3eba90bb63e1eb5ab9

This commit is contained in:
Ankit Songara 2026-08-19 23:09:58 -07:00 committed by GitHub
commit 8b15b74ef3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 31 additions and 4 deletions

View File

@ -10,6 +10,7 @@ import (
"html/template"
"net/http"
"unicode"
"unicode/utf16"
"github.com/gin-gonic/gin/codec/json"
"github.com/gin-gonic/gin/internal/bytesconv"
@ -160,14 +161,22 @@ func (r AsciiJSON) Render(w http.ResponseWriter) error {
}
var buffer bytes.Buffer
escapeBuf := make([]byte, 0, 6) // Preallocate 6 bytes for Unicode escape sequences
escapeBuf := make([]byte, 0, 12) // Preallocate 12 bytes: worst case is a \uXXXX\uXXXX surrogate pair
for _, r := range bytesconv.BytesToString(ret) {
if r > unicode.MaxASCII {
switch {
case r <= unicode.MaxASCII:
buffer.WriteByte(byte(r))
case r <= 0xFFFF:
escapeBuf = fmt.Appendf(escapeBuf[:0], "\\u%04x", r) // Reuse escapeBuf
buffer.Write(escapeBuf)
} else {
buffer.WriteByte(byte(r))
default:
// Code points outside the Basic Multilingual Plane don't fit in a
// single 4-hex-digit \u escape; RFC 8259 §7 requires encoding them
// as a UTF-16 surrogate pair instead.
r1, r2 := utf16.EncodeRune(r)
escapeBuf = fmt.Appendf(escapeBuf[:0], "\\u%04x\\u%04x", r1, r2)
buffer.Write(escapeBuf)
}
}

View File

@ -5,6 +5,7 @@
package render
import (
"encoding/json"
"encoding/xml"
"errors"
"html/template"
@ -261,6 +262,23 @@ func TestRenderAsciiJSON(t *testing.T) {
assert.Equal(t, "3.1415926", w2.Body.String())
}
func TestRenderAsciiJSONNonBMP(t *testing.T) {
w := httptest.NewRecorder()
data := map[string]any{"msg": "😀"}
err := (AsciiJSON{data}).Render(w)
require.NoError(t, err)
// U+1F600 is outside the Basic Multilingual Plane and must be encoded as
// a UTF-16 surrogate pair (RFC 8259 §7), not a single \u escape with 5+
// hex digits.
assert.JSONEq(t, "{\"msg\":\"\\ud83d\\ude00\"}", w.Body.String())
var decoded map[string]string
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &decoded))
assert.Equal(t, "😀", decoded["msg"])
}
func TestRenderAsciiJSONFail(t *testing.T) {
w := httptest.NewRecorder()
data := make(chan int)