response.go

Summary

Maintainability
A
25 mins
Test Coverage
package vox

import (
    "mime"
    "net/http"
    "net/url"
    "path"
    "strings"
)

var (
    explicitSetBody   = struct{}{}
    explicitSetStatus = 0
)

var htmlReplacer = strings.NewReplacer(
    "&", "&",
    "<", "&lt;",
    ">", "&gt;",
    `"`, "&#34;",
    "'", "&#39;",
)

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(),
    }
}