152 lines
3.5 KiB
Go
152 lines
3.5 KiB
Go
package osin
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
)
|
|
|
|
// Data for response output
|
|
type ResponseData map[string]interface{}
|
|
|
|
// Response type enum
|
|
type ResponseType int
|
|
|
|
const (
|
|
DATA ResponseType = iota
|
|
REDIRECT
|
|
)
|
|
|
|
// Server response
|
|
type Response struct {
|
|
Type ResponseType
|
|
StatusCode int
|
|
StatusText string
|
|
ErrorStatusCode int
|
|
URL string
|
|
Output ResponseData
|
|
Headers http.Header
|
|
IsError bool
|
|
ErrorId string
|
|
InternalError error
|
|
RedirectInFragment bool
|
|
|
|
// Storage to use in this response - required
|
|
Storage Storage
|
|
}
|
|
|
|
func NewResponse(storage Storage) *Response {
|
|
r := &Response{
|
|
Type: DATA,
|
|
StatusCode: 200,
|
|
ErrorStatusCode: 200,
|
|
Output: make(ResponseData),
|
|
Headers: make(http.Header),
|
|
IsError: false,
|
|
Storage: storage.Clone(),
|
|
}
|
|
r.Headers.Add(
|
|
"Cache-Control",
|
|
"no-cache, no-store, max-age=0, must-revalidate",
|
|
)
|
|
r.Headers.Add("Pragma", "no-cache")
|
|
r.Headers.Add("Expires", "Fri, 01 Jan 1990 00:00:00 GMT")
|
|
return r
|
|
}
|
|
|
|
// SetError sets an error id and description on the Response
|
|
// state and uri are left blank
|
|
func (r *Response) SetError(id string, description string) {
|
|
r.SetErrorUri(id, description, "", "")
|
|
}
|
|
|
|
// SetErrorState sets an error id, description, and state on the Response
|
|
// uri is left blank
|
|
func (r *Response) SetErrorState(id string, description string, state string) {
|
|
r.SetErrorUri(id, description, "", state)
|
|
}
|
|
|
|
// SetErrorUri sets an error id, description, state, and uri on the Response
|
|
func (r *Response) SetErrorUri(id string, description string, uri string, state string) {
|
|
// get default error message
|
|
if description == "" {
|
|
description = deferror.Get(id)
|
|
}
|
|
|
|
// set error parameters
|
|
r.IsError = true
|
|
r.ErrorId = id
|
|
r.StatusCode = r.ErrorStatusCode
|
|
if r.StatusCode != 200 {
|
|
r.StatusText = description
|
|
} else {
|
|
r.StatusText = ""
|
|
}
|
|
r.Output = make(ResponseData) // clear output
|
|
r.Output["error"] = id
|
|
r.Output["error_description"] = description
|
|
if uri != "" {
|
|
r.Output["error_uri"] = uri
|
|
}
|
|
if state != "" {
|
|
r.Output["state"] = state
|
|
}
|
|
}
|
|
|
|
// SetRedirect changes the response to redirect to the given url
|
|
func (r *Response) SetRedirect(url string) {
|
|
// set redirect parameters
|
|
r.Type = REDIRECT
|
|
r.URL = url
|
|
}
|
|
|
|
// SetRedirectFragment sets redirect values to be passed in fragment instead of as query parameters
|
|
func (r *Response) SetRedirectFragment(f bool) {
|
|
r.RedirectInFragment = f
|
|
}
|
|
|
|
// GetRedirectUrl returns the redirect url with all query string parameters
|
|
func (r *Response) GetRedirectUrl() (string, error) {
|
|
if r.Type != REDIRECT {
|
|
return "", errors.New("Not a redirect response")
|
|
}
|
|
|
|
u, err := url.Parse(r.URL)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
var q url.Values
|
|
if r.RedirectInFragment {
|
|
// start with empty set for fragment
|
|
q = url.Values{}
|
|
} else {
|
|
// add parameters to existing query
|
|
q = u.Query()
|
|
}
|
|
|
|
// add parameters
|
|
for n, v := range r.Output {
|
|
q.Set(n, fmt.Sprint(v))
|
|
}
|
|
|
|
// https://tools.ietf.org/html/rfc6749#section-4.2.2
|
|
// Fragment should be encoded as application/x-www-form-urlencoded (%-escaped, spaces are represented as '+')
|
|
// The stdlib URL#String() doesn't make that easy to accomplish, so build this ourselves
|
|
if r.RedirectInFragment {
|
|
u.Fragment = ""
|
|
redirectURI := u.String() + "#" + q.Encode()
|
|
return redirectURI, nil
|
|
}
|
|
|
|
// Otherwise, update the query and encode normally
|
|
u.RawQuery = q.Encode()
|
|
u.Fragment = ""
|
|
return u.String(), nil
|
|
}
|
|
|
|
func (r *Response) Close() {
|
|
r.Storage.Close()
|
|
}
|