Merge 19dff1a81eef28989a427ef31171f104c8857640 into ae15646aba14cd8245fbebd263cc7740c6789ef3

This commit is contained in:
Erik Jansson 2024-03-05 13:21:26 +01:00 committed by GitHub
commit 9d13626ec2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 86 additions and 7 deletions

View File

@ -15,6 +15,9 @@ type Data struct {
// Render (Data) writes data with custom ContentType.
func (r Data) Render(w http.ResponseWriter) (err error) {
r.WriteContentType(w)
writeContentLength(w, len(r.Data))
_, err = w.Write(r.Data)
return
}

View File

@ -62,6 +62,11 @@ func (r JSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonContentType)
}
// WriteContentType (JSON) writes JSON ContentType.
func (r JSON) WriteContentLength(w http.ResponseWriter, length int) {
writeContentLength(w, length)
}
// WriteJSON marshals the given interface object and writes it with custom ContentType.
func WriteJSON(w http.ResponseWriter, obj any) error {
writeContentType(w, jsonContentType)
@ -69,6 +74,9 @@ func WriteJSON(w http.ResponseWriter, obj any) error {
if err != nil {
return err
}
writeContentLength(w, len(jsonBytes))
_, err = w.Write(jsonBytes)
return err
}
@ -80,6 +88,9 @@ func (r IndentedJSON) Render(w http.ResponseWriter) error {
if err != nil {
return err
}
writeContentLength(w, len(jsonBytes))
_, err = w.Write(jsonBytes)
return err
}
@ -96,13 +107,22 @@ func (r SecureJSON) Render(w http.ResponseWriter) error {
if err != nil {
return err
}
length := len(jsonBytes)
// if the jsonBytes is array values
if bytes.HasPrefix(jsonBytes, bytesconv.StringToBytes("[")) && bytes.HasSuffix(jsonBytes,
bytesconv.StringToBytes("]")) {
if _, err = w.Write(bytesconv.StringToBytes(r.Prefix)); err != nil {
return err
}
length += len(r.Prefix)
}
writeContentLength(w, length)
_, err = w.Write(jsonBytes)
return err
}
@ -115,18 +135,24 @@ func (r SecureJSON) WriteContentType(w http.ResponseWriter) {
// Render (JsonpJSON) marshals the given interface object and writes it and its callback with custom ContentType.
func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
r.WriteContentType(w)
ret, err := json.Marshal(r.Data)
jsonBytes, err := json.Marshal(r.Data)
if err != nil {
return err
}
length := len(jsonBytes)
if r.Callback == "" {
_, err = w.Write(ret)
writeContentLength(w, length)
_, err = w.Write(jsonBytes)
return err
}
callback := template.JSEscapeString(r.Callback)
if _, err = w.Write(bytesconv.StringToBytes(callback)); err != nil {
callbackBytes := bytesconv.StringToBytes(callback)
if _, err = w.Write(callbackBytes); err != nil {
return err
}
@ -134,7 +160,7 @@ func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
return err
}
if _, err = w.Write(ret); err != nil {
if _, err = w.Write(jsonBytes); err != nil {
return err
}
@ -142,6 +168,10 @@ func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
return err
}
length += len(callbackBytes) + 3 // 3 = len("();")
writeContentLength(w, length)
return nil
}
@ -167,7 +197,11 @@ func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
buffer.WriteString(cvt)
}
_, err = w.Write(buffer.Bytes())
jsonBytes := buffer.Bytes()
writeContentLength(w, len(jsonBytes))
_, err = w.Write(jsonBytes)
return err
}

View File

@ -26,6 +26,8 @@ func (r ProtoBuf) Render(w http.ResponseWriter) error {
return err
}
writeContentLength(w, len(bytes))
_, err = w.Write(bytes)
return err
}

View File

@ -4,7 +4,10 @@
package render
import "net/http"
import (
"net/http"
"strconv"
)
// Render interface is to be implemented by JSON, XML, HTML, YAML and so on.
type Render interface {
@ -39,3 +42,7 @@ func writeContentType(w http.ResponseWriter, value []string) {
header["Content-Type"] = value
}
}
func writeContentLength(w http.ResponseWriter, value int) {
w.Header().Set("Content-Length", strconv.Itoa(value))
}

View File

@ -39,6 +39,8 @@ func TestRenderJSON(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
assert.Equal(t, "36", w.Header().Get("Content-Length"))
}
func TestRenderJSONError(t *testing.T) {
@ -61,6 +63,8 @@ func TestRenderIndentedJSON(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\"\n}", w.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
assert.Equal(t, "38", w.Header().Get("Content-Length"))
}
func TestRenderIndentedJSONPanics(t *testing.T) {
@ -86,6 +90,7 @@ func TestRenderSecureJSON(t *testing.T) {
assert.NoError(t, err1)
assert.Equal(t, "{\"foo\":\"bar\"}", w1.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w1.Header().Get("Content-Type"))
assert.Equal(t, "13", w1.Header().Get("Content-Length"))
w2 := httptest.NewRecorder()
datas := []map[string]any{{
@ -98,6 +103,7 @@ func TestRenderSecureJSON(t *testing.T) {
assert.NoError(t, err2)
assert.Equal(t, "while(1);[{\"foo\":\"bar\"},{\"bar\":\"foo\"}]", w2.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w2.Header().Get("Content-Type"))
assert.Equal(t, "38", w2.Header().Get("Content-Length"))
}
func TestRenderSecureJSONFail(t *testing.T) {
@ -123,6 +129,7 @@ func TestRenderJsonpJSON(t *testing.T) {
assert.NoError(t, err1)
assert.Equal(t, "x({\"foo\":\"bar\"});", w1.Body.String())
assert.Equal(t, "application/javascript; charset=utf-8", w1.Header().Get("Content-Type"))
assert.Equal(t, "17", w1.Header().Get("Content-Length"))
w2 := httptest.NewRecorder()
datas := []map[string]any{{
@ -135,6 +142,8 @@ func TestRenderJsonpJSON(t *testing.T) {
assert.NoError(t, err2)
assert.Equal(t, "x([{\"foo\":\"bar\"},{\"bar\":\"foo\"}]);", w2.Body.String())
assert.Equal(t, "application/javascript; charset=utf-8", w2.Header().Get("Content-Type"))
assert.Equal(t, "33", w2.Header().Get("Content-Length"))
}
type errorWriter struct {
@ -195,6 +204,7 @@ func TestRenderJsonpJSONError2(t *testing.T) {
assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type"))
assert.Equal(t, "13", w.Header().Get("Content-Length"))
}
func TestRenderJsonpJSONFail(t *testing.T) {
@ -218,6 +228,7 @@ func TestRenderAsciiJSON(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, "{\"lang\":\"GO\\u8bed\\u8a00\",\"tag\":\"\\u003cbr\\u003e\"}", w1.Body.String())
assert.Equal(t, "application/json", w1.Header().Get("Content-Type"))
assert.Equal(t, "48", w1.Header().Get("Content-Length"))
w2 := httptest.NewRecorder()
data2 := 3.1415926
@ -225,6 +236,8 @@ func TestRenderAsciiJSON(t *testing.T) {
err = (AsciiJSON{data2}).Render(w2)
assert.NoError(t, err)
assert.Equal(t, "3.1415926", w2.Body.String())
assert.Equal(t, "9", w2.Header().Get("Content-Length"))
}
func TestRenderAsciiJSONFail(t *testing.T) {
@ -286,6 +299,8 @@ b:
assert.NoError(t, err)
assert.Equal(t, "|4-\n a : Easy!\n b:\n \tc: 2\n \td: [3, 4]\n \t\n", w.Body.String())
assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type"))
assert.Equal(t, "56", w.Header().Get("Content-Length"))
}
type fail struct{}
@ -314,6 +329,8 @@ func TestRenderTOML(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, "foo = 'bar'\nhtml = '<b>'\n", w.Body.String())
assert.Equal(t, "application/toml; charset=utf-8", w.Header().Get("Content-Type"))
assert.Equal(t, "25", w.Header().Get("Content-Length"))
}
func TestRenderTOMLFail(t *testing.T) {
@ -342,6 +359,8 @@ func TestRenderProtoBuf(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, string(protoData), w.Body.String())
assert.Equal(t, "application/x-protobuf", w.Header().Get("Content-Type"))
assert.Equal(t, strconv.Itoa(len(string(protoData))), w.Header().Get("Content-Length"))
}
func TestRenderProtoBufFail(t *testing.T) {
@ -419,6 +438,8 @@ func TestRenderData(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, "#!PNG some raw data", w.Body.String())
assert.Equal(t, "image/png", w.Header().Get("Content-Type"))
assert.Equal(t, "19", w.Header().Get("Content-Length"))
}
func TestRenderString(t *testing.T) {
@ -451,6 +472,8 @@ func TestRenderStringLenZero(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, "hola %s %d", w.Body.String())
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
assert.Equal(t, "10", w.Header().Get("Content-Length"))
}
func TestRenderHTMLTemplate(t *testing.T) {

View File

@ -36,6 +36,12 @@ func WriteString(w http.ResponseWriter, format string, data []any) (err error) {
_, err = fmt.Fprintf(w, format, data...)
return
}
_, err = w.Write(bytesconv.StringToBytes(format))
bytes := bytesconv.StringToBytes(format)
writeContentLength(w, len(bytes))
_, err = w.Write(bytes)
return
}

View File

@ -26,6 +26,8 @@ func (r TOML) Render(w http.ResponseWriter) error {
return err
}
writeContentLength(w, len(bytes))
_, err = w.Write(bytes)
return err
}

View File

@ -26,6 +26,8 @@ func (r YAML) Render(w http.ResponseWriter) error {
return err
}
writeContentLength(w, len(bytes))
_, err = w.Write(bytes)
return err
}