1// Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
2// Use of this source code is governed by a MIT style
3// license that can be found in the LICENSE file.
4
5package render
6
7import (
8	"bytes"
9	"fmt"
10	"html/template"
11	"net/http"
12
13	"github.com/gin-gonic/gin/internal/bytesconv"
14	"github.com/gin-gonic/gin/internal/json"
15)
16
17// JSON contains the given interface object.
18type JSON struct {
19	Data interface{}
20}
21
22// IndentedJSON contains the given interface object.
23type IndentedJSON struct {
24	Data interface{}
25}
26
27// SecureJSON contains the given interface object and its prefix.
28type SecureJSON struct {
29	Prefix string
30	Data   interface{}
31}
32
33// JsonpJSON contains the given interface object its callback.
34type JsonpJSON struct {
35	Callback string
36	Data     interface{}
37}
38
39// AsciiJSON contains the given interface object.
40type AsciiJSON struct {
41	Data interface{}
42}
43
44// SecureJSONPrefix is a string which represents SecureJSON prefix.
45type SecureJSONPrefix string
46
47// PureJSON contains the given interface object.
48type PureJSON struct {
49	Data interface{}
50}
51
52var jsonContentType = []string{"application/json; charset=utf-8"}
53var jsonpContentType = []string{"application/javascript; charset=utf-8"}
54var jsonAsciiContentType = []string{"application/json"}
55
56// Render (JSON) writes data with custom ContentType.
57func (r JSON) Render(w http.ResponseWriter) (err error) {
58	if err = WriteJSON(w, r.Data); err != nil {
59		panic(err)
60	}
61	return
62}
63
64// WriteContentType (JSON) writes JSON ContentType.
65func (r JSON) WriteContentType(w http.ResponseWriter) {
66	writeContentType(w, jsonContentType)
67}
68
69// WriteJSON marshals the given interface object and writes it with custom ContentType.
70func WriteJSON(w http.ResponseWriter, obj interface{}) error {
71	writeContentType(w, jsonContentType)
72	jsonBytes, err := json.Marshal(obj)
73	if err != nil {
74		return err
75	}
76	_, err = w.Write(jsonBytes)
77	return err
78}
79
80// Render (IndentedJSON) marshals the given interface object and writes it with custom ContentType.
81func (r IndentedJSON) Render(w http.ResponseWriter) error {
82	r.WriteContentType(w)
83	jsonBytes, err := json.MarshalIndent(r.Data, "", "    ")
84	if err != nil {
85		return err
86	}
87	_, err = w.Write(jsonBytes)
88	return err
89}
90
91// WriteContentType (IndentedJSON) writes JSON ContentType.
92func (r IndentedJSON) WriteContentType(w http.ResponseWriter) {
93	writeContentType(w, jsonContentType)
94}
95
96// Render (SecureJSON) marshals the given interface object and writes it with custom ContentType.
97func (r SecureJSON) Render(w http.ResponseWriter) error {
98	r.WriteContentType(w)
99	jsonBytes, err := json.Marshal(r.Data)
100	if err != nil {
101		return err
102	}
103	// if the jsonBytes is array values
104	if bytes.HasPrefix(jsonBytes, bytesconv.StringToBytes("[")) && bytes.HasSuffix(jsonBytes,
105		bytesconv.StringToBytes("]")) {
106		_, err = w.Write(bytesconv.StringToBytes(r.Prefix))
107		if err != nil {
108			return err
109		}
110	}
111	_, err = w.Write(jsonBytes)
112	return err
113}
114
115// WriteContentType (SecureJSON) writes JSON ContentType.
116func (r SecureJSON) WriteContentType(w http.ResponseWriter) {
117	writeContentType(w, jsonContentType)
118}
119
120// Render (JsonpJSON) marshals the given interface object and writes it and its callback with custom ContentType.
121func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
122	r.WriteContentType(w)
123	ret, err := json.Marshal(r.Data)
124	if err != nil {
125		return err
126	}
127
128	if r.Callback == "" {
129		_, err = w.Write(ret)
130		return err
131	}
132
133	callback := template.JSEscapeString(r.Callback)
134	_, err = w.Write(bytesconv.StringToBytes(callback))
135	if err != nil {
136		return err
137	}
138	_, err = w.Write(bytesconv.StringToBytes("("))
139	if err != nil {
140		return err
141	}
142	_, err = w.Write(ret)
143	if err != nil {
144		return err
145	}
146	_, err = w.Write(bytesconv.StringToBytes(");"))
147	if err != nil {
148		return err
149	}
150
151	return nil
152}
153
154// WriteContentType (JsonpJSON) writes Javascript ContentType.
155func (r JsonpJSON) WriteContentType(w http.ResponseWriter) {
156	writeContentType(w, jsonpContentType)
157}
158
159// Render (AsciiJSON) marshals the given interface object and writes it with custom ContentType.
160func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
161	r.WriteContentType(w)
162	ret, err := json.Marshal(r.Data)
163	if err != nil {
164		return err
165	}
166
167	var buffer bytes.Buffer
168	for _, r := range bytesconv.BytesToString(ret) {
169		cvt := string(r)
170		if r >= 128 {
171			cvt = fmt.Sprintf("\\u%04x", int64(r))
172		}
173		buffer.WriteString(cvt)
174	}
175
176	_, err = w.Write(buffer.Bytes())
177	return err
178}
179
180// WriteContentType (AsciiJSON) writes JSON ContentType.
181func (r AsciiJSON) WriteContentType(w http.ResponseWriter) {
182	writeContentType(w, jsonAsciiContentType)
183}
184
185// Render (PureJSON) writes custom ContentType and encodes the given interface object.
186func (r PureJSON) Render(w http.ResponseWriter) error {
187	r.WriteContentType(w)
188	encoder := json.NewEncoder(w)
189	encoder.SetEscapeHTML(false)
190	return encoder.Encode(r.Data)
191}
192
193// WriteContentType (PureJSON) writes custom ContentType.
194func (r PureJSON) WriteContentType(w http.ResponseWriter) {
195	writeContentType(w, jsonContentType)
196}
197