148 lines
3.7 KiB
Go
148 lines
3.7 KiB
Go
package http
|
|
|
|
import "io"
|
|
import "fmt"
|
|
import "bytes"
|
|
import "net/http"
|
|
|
|
// Error represents an HTTP error. It is passed to error documents as data.
|
|
// If returned from a template, the server will show the appropriate error page.
|
|
type Error struct {
|
|
Status int
|
|
Message any
|
|
}
|
|
|
|
func (err Error) Error () string {
|
|
message := fmt.Sprint(err.Message)
|
|
text := http.StatusText(err.Status)
|
|
if message == "" {
|
|
return fmt.Sprintf("%d: %s", err.Status, text)
|
|
} else {
|
|
return fmt.Sprintf("%d: %s: %s", err.Status, text, message)
|
|
}
|
|
}
|
|
|
|
// Redirect, if returned from a template, will cause the server to redirect the
|
|
// client to the given location.
|
|
type Redirect struct {
|
|
Status int
|
|
Location string
|
|
}
|
|
|
|
func (err Redirect) Error () string {
|
|
return fmt.Sprintf (
|
|
"%d: %s: %s",
|
|
err.Status, http.StatusText(err.Status), err.Location)
|
|
}
|
|
|
|
// HTTPData represents information about an ongoing HTTP request that is made
|
|
// available to templates as they are being executed.
|
|
type HTTPData struct {
|
|
// Res is like an http.ResponseWriter.
|
|
Res WrappedResponseWriter
|
|
// Req is the HTTP request.
|
|
Req *http.Request
|
|
}
|
|
|
|
type WrappedResponseWriter struct {
|
|
responseWriter http.ResponseWriter
|
|
resetFunc func ()
|
|
Header WrappedHeader
|
|
}
|
|
|
|
func (this WrappedResponseWriter) WriteHeader (statusCode int) string {
|
|
this.responseWriter.WriteHeader(statusCode)
|
|
return ""
|
|
}
|
|
|
|
func (this WrappedResponseWriter) Reset () string {
|
|
this.resetFunc()
|
|
return ""
|
|
}
|
|
|
|
type WrappedHeader struct { http.Header }
|
|
|
|
func (this WrappedHeader) Add (name, value string) string {
|
|
this.Header.Add(name, value)
|
|
return ""
|
|
}
|
|
|
|
func (this WrappedHeader) Del (name string) string {
|
|
this.Header.Del(name)
|
|
return ""
|
|
}
|
|
|
|
func (this WrappedHeader) Set (name, value string) string {
|
|
this.Header.Set(name, value)
|
|
return ""
|
|
}
|
|
|
|
// UnderlyingResponseWriter returns the underlying http.ResponseWriter of a
|
|
// WrappedResponseWriter. This is a separate function because it should not be
|
|
// directly accessible to templates.
|
|
func UnderlyingResponseWriter (writer WrappedResponseWriter) http.ResponseWriter {
|
|
return writer.responseWriter
|
|
}
|
|
|
|
var _ http.ResponseWriter = new(HTTPResponseRecorder)
|
|
|
|
// HTTPResponseRecorder is an http.ResponseWriter that can buffer a response to
|
|
// be played back later.
|
|
type HTTPResponseRecorder struct {
|
|
Status int
|
|
Head http.Header
|
|
buffer bytes.Buffer
|
|
}
|
|
|
|
func (this *HTTPResponseRecorder) Header () http.Header {
|
|
if this.Head == nil {
|
|
this.Head = make(http.Header)
|
|
}
|
|
return this.Head
|
|
}
|
|
|
|
func (this *HTTPResponseRecorder) Write (buffer []byte) (int, error) {
|
|
return this.buffer.Write(buffer)
|
|
}
|
|
|
|
func (this *HTTPResponseRecorder) WriteHeader (statusCode int) {
|
|
this.Status = statusCode
|
|
}
|
|
// Play replays the response in its entirety to res.
|
|
func (this *HTTPResponseRecorder) Play (res http.ResponseWriter) error {
|
|
this.WriteHeaderValues(res)
|
|
this.WriteHeaderStatus(res)
|
|
_, err := this.WriteBody(res)
|
|
return err
|
|
}
|
|
|
|
// WriteHeaderValues adds all header values in the recorder to res, replacing
|
|
// them if they exist already.
|
|
func (this *HTTPResponseRecorder) WriteHeaderValues (res http.ResponseWriter) {
|
|
header := res.Header()
|
|
for name, value := range this.Head {
|
|
header[name] = value
|
|
}
|
|
}
|
|
|
|
// WriteHeaderStatus calls res.WriteHeader with the status code in the recorder.
|
|
func (this *HTTPResponseRecorder) WriteHeaderStatus (res http.ResponseWriter) {
|
|
status := this.Status
|
|
if status == 0 {
|
|
status = http.StatusOK
|
|
}
|
|
res.WriteHeader(status)
|
|
}
|
|
|
|
// WriteBody writes the body data within the recorder to res.
|
|
func (this *HTTPResponseRecorder) WriteBody (res http.ResponseWriter) (int64, error) {
|
|
return io.Copy(res, &this.buffer)
|
|
}
|
|
|
|
// Reset resets this response recorder so it can be used again.
|
|
func (this *HTTPResponseRecorder) Reset () {
|
|
this.buffer.Reset()
|
|
this.Head = nil
|
|
this.Status = http.StatusOK
|
|
}
|