response.go
package vox
import (
"mime"
"net/http"
"net/url"
"path"
"strings"
)
var (
explicitSetBody = struct{}{}
explicitSetStatus = 0
)
var htmlReplacer = strings.NewReplacer(
"&", "&",
"<", "<",
">", ">",
`"`, """,
"'", "'",
)
func htmlEscape(s string) string {
return htmlReplacer.Replace(s)
}
// A Response object contains all the information which will written to current
// HTTP client.
type Response struct {
request *Request
// Don't write headers, status and body from Response struct to client. In
// the case of you're using the go's origin http.Response.
DontRespond bool
// Writer is the raw http.ResponseWriter for current request. You should
// assign the Body / Status / Header value instead of using this field.
Writer http.ResponseWriter
// Body is the container for HTTP response's body.
Body interface{}
// The status code which will respond as the HTTP Response's status code.
// 200 will be used as the default value if not set.
Status int
// Headers which will be written to the response.
Header http.Header
}
func (response *Response) setImplicitContentType() {
if response.Header.Get("Content-Type") != "" {
return
}
if response.Body == explicitSetBody {
return
}
switch response.Body.(type) {
case []byte:
case string:
default:
response.Header.Set("Content-Type", mime.TypeByExtension(".json"))
}
}
var parseURL = url.Parse
// Redirect request to another url.
func (response *Response) Redirect(url string, code int) {
request := response.request
if u, err := parseURL(url); err == nil {
if u.Scheme == "" && u.Host == "" {
oldpath := request.URL.Path
if oldpath == "" {
oldpath = "/"
}
if url == "" || url[0] != '/' {
olddir, _ := path.Split(oldpath)
url = olddir + url
}
var query string
if i := strings.Index(url, "?"); i != -1 {
url, query = url[:i], url[i:]
}
trailing := strings.HasSuffix(url, "/")
url = path.Clean(url)
if trailing && !strings.HasSuffix(url, "/") {
url += "/"
}
url += query
}
}
response.Header.Set("Location", url)
if request.Method == "GET" || request.Method == "HEAD" {
response.Header.Set("Content-Type", "text/html; charset=utf-8")
}
response.Status = code
if request.Method == "GET" {
response.Body = "<a href=\"" + htmlEscape(url) + "\">" + http.StatusText(code) + "</a>.\n"
}
}
// SetCookie sets cookies on response.
func (response *Response) SetCookie(cookie *http.Cookie) {
if v := cookie.String(); v != "" {
response.Header.Add("Set-Cookie", v)
}
}
func (response *Response) setImplicitBody() {
if response.Body == explicitSetBody {
response.Body = http.StatusText(404)
}
}
func (response *Response) setImplicitStatus() {
if response.Status != explicitSetStatus {
return
}
if response.Body == explicitSetBody {
response.Status = 404
return
}
if _, ok := response.Body.(error); ok {
response.Status = 500
return
}
response.Status = 200
}
func (response *Response) setImplicit() {
response.setImplicitContentType()
response.setImplicitStatus()
response.setImplicitBody()
}
func createResponse(rw http.ResponseWriter) *Response {
return &Response{
Writer: rw,
Body: explicitSetBody,
Status: explicitSetStatus,
Header: rw.Header(),
}
}