This repository was archived by the owner on Apr 17, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathrack_response.go
More file actions
124 lines (91 loc) · 2.01 KB
/
rack_response.go
File metadata and controls
124 lines (91 loc) · 2.01 KB
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package gorack
import (
"bytes"
"io"
"io/ioutil"
"net/http"
"strconv"
"strings"
)
// delimiter used to separate headers from one another
// and body from the rest of the headers
const delim = "\x00"
type RackResponse struct {
rackResponse io.Reader
Headers http.Header
StatusCode int
Body io.Reader
}
func NewRackResponse(r io.Reader) *RackResponse {
return &RackResponse{rackResponse: r}
}
func (r *RackResponse) Parse() error {
// while determining headers size
// read(tee) headers into separate
// buffer for futher processing
headerBuffer := &bytes.Buffer{}
reader := io.TeeReader(r.rackResponse, headerBuffer)
// read char by char to correctly land at body start
buf := make([]byte, 1, 1)
// end of headers, end of line
eol, eoh := false, false
for {
_, err := reader.Read(buf)
if err == io.EOF {
break
}
if err != nil {
return err
}
char := string(buf[0]) // single char
// eoh marks end of headers
eoh = eol && delim == char
if eoh {
break
}
eol = delim == char
}
code, headers, err := parseHeaders(headerBuffer)
if err != nil {
return err
}
r.StatusCode = code
r.Headers = headers
r.Body = r.rackResponse
return nil
}
func parseHeaders(buf io.Reader) (int, http.Header, error) {
headers, err := ioutil.ReadAll(buf)
if err != nil {
return 0, nil, err
}
lines := bytes.Split(headers, []byte(delim))
// first header is a status code
code, err := strconv.Atoi(string(lines[0]))
if err != nil {
return 0, nil, err
}
hdrs := http.Header{}
for _, line := range lines[1:] {
hdr := string(line)
if hdr == "" {
continue
}
kvs := strings.SplitN(hdr, ": ", 2)
hdrs.Add(kvs[0], kvs[1])
}
return code, hdrs, nil
}
func (r *RackResponse) WriteTo(w http.ResponseWriter) error {
if err := r.Parse(); err != nil {
return err
}
headers := w.Header()
for name, _ := range r.Headers {
//TODO: add test
headers.Set(name, r.Headers.Get(name))
}
w.WriteHeader(r.StatusCode)
_, err := io.Copy(w, r.Body)
return err
}