summaryrefslogtreecommitdiff
path: root/spartan/response.go
blob: edc1db626819dee79dd1eb4c120d26ed6a1f52de (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package spartan

import (
	"bytes"
	"io"
	"sync"

	"tildegit.org/tjp/gus"
)

// The spartan response types.
const (
	StatusSuccess     gus.Status = 2
	StatusRedirect    gus.Status = 3
	StatusClientError gus.Status = 4
	StatusServerError gus.Status = 5
)

// Success builds a successful spartan response.
func Success(mediatype string, body io.Reader) *gus.Response {
	return &gus.Response{
		Status: StatusSuccess,
		Meta:   mediatype,
		Body:   body,
	}
}

// Redirect builds a spartan redirect response.
func Redirect(url string) *gus.Response {
	return &gus.Response{
		Status: StatusRedirect,
		Meta:   url,
	}
}

// ClientError builds a "client error" spartan response.
func ClientError(err error) *gus.Response {
	return &gus.Response{
		Status: StatusClientError,
		Meta: err.Error(),
	}
}

// ServerError builds a "server error" spartan response.
func ServerError(err error) *gus.Response {
	return &gus.Response{
		Status: StatusServerError,
		Meta: err.Error(),
	}
}

// NewResponseReader builds a reader for a response.
func NewResponseReader(response *gus.Response) gus.ResponseReader {
	return &responseReader{
		Response: response,
		once:     &sync.Once{},
	}
}

type responseReader struct {
	*gus.Response
	reader io.Reader
	once   *sync.Once
}

func (rdr *responseReader) Read(b []byte) (int, error) {
	rdr.ensureReader()
	return rdr.reader.Read(b)
}

func (rdr *responseReader) WriteTo(dst io.Writer) (int64, error) {
	rdr.ensureReader()
	return rdr.reader.(io.WriterTo).WriteTo(dst)
}

func (rdr *responseReader) ensureReader() {
	rdr.once.Do(func() {
		hdr := bytes.NewBuffer(rdr.headerLine())
		if rdr.Body != nil {
			rdr.reader = io.MultiReader(hdr, rdr.Body)
		} else {
			rdr.reader = hdr
		}
	})
}

func (rdr *responseReader) headerLine() []byte {
	meta := rdr.Meta.(string)
	buf := make([]byte, len(meta)+4)
	buf[0] = byte(rdr.Status) + '0'
	buf[1] = ' '
	copy(buf[2:], meta)
	buf[len(buf)-2] = '\r'
	buf[len(buf)-1] = '\n'
	return buf
}