-
Notifications
You must be signed in to change notification settings - Fork 0
/
response.go
142 lines (123 loc) · 4.75 KB
/
response.go
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package ngebut
import (
"bytes"
"github.com/panjf2000/gnet/v2"
"io"
"net/http"
"strconv"
)
type Response struct {
Status string // e.g. "200 OK"
StatusCode int // e.g. 200
Proto string // e.g. "HTTP/1.0"
ProtoMajor int // e.g. 1
ProtoMinor int // e.g. 0
// Header maps header keys to values. If the response had multiple
// headers with the same key, they may be concatenated, with comma
// delimiters. (RFC 7230, section 3.2.2 requires that multiple headers
// be semantically equivalent to a comma-delimited sequence.) When
// Header values are duplicated by other fields in this struct (e.g.,
// ContentLength, TransferEncoding, Trailer), the field values are
// authoritative.
//
// Keys in the map are canonicalized (see CanonicalHeaderKey).
Header Header
// Body represents the response body.
//
// The response body is streamed on demand as the Body field
// is read. If the network connection fails or the server
// terminates the response, Body.Read calls return an error.
//
// The http Client and Transport guarantee that Body is always
// non-nil, even on responses without a body or responses with
// a zero-length body. It is the caller's responsibility to
// close Body. The default HTTP client's Transport may not
// reuse HTTP/1.x "keep-alive" TCP connections if the Body is
// not read to completion and closed.
//
// The Body is automatically dechunked if the server replied
// with a "chunked" Transfer-Encoding.
//
// As of Go 1.12, the Body will also implement io.Writer
// on a successful "101 Switching Protocols" response,
// as used by WebSockets and HTTP/2's "h2c" mode.
Body io.ReadCloser
// ContentLength records the length of the associated content. The
// value -1 indicates that the length is unknown. Unless Request.Method
// is "HEAD", values >= 0 indicate that the given number of bytes may
// be read from Body.
ContentLength int64
// Contains transfer encodings from outer-most to inner-most. Value is
// nil, means that "identity" encoding is used.
TransferEncoding []string
// Close records whether the header directed that the connection be
// closed after reading Body. The value is advice for clients: neither
// ReadResponse nor Response.Write ever closes a connection.
Close bool
// Uncompressed reports whether the response was sent compressed but
// was decompressed by the http package. When true, reading from
// Body yields the uncompressed content instead of the compressed
// content actually set from the server, ContentLength is set to -1,
// and the "Content-Length" and "Content-Encoding" fields are deleted
// from the responseHeader. To get the original response from
// the server, set Transport.DisableCompression to true.
Uncompressed bool
// Trailer maps trailer keys to values in the same
// format as Header.
//
// The Trailer initially contains only nil values, one for
// each key specified in the server's "Trailer" header
// value. Those values are not added to Header.
//
// Trailer must not be accessed concurrently with Read calls
// on the Body.
//
// After Body.Read has returned io.EOF, Trailer will contain
// any trailer values sent by the server.
Trailer Header
// Request is the request that was sent to obtain this Response.
// Request's Body is nil (having already been consumed).
// This is only populated for Client requests.
Request *Request
}
type responseWriter struct {
conn gnet.Conn
Response *Response
}
func (w *responseWriter) Header() Header {
return w.Response.Header
}
func (w *responseWriter) Write(b []byte) (int, error) {
w.Response.Header.Set("Content-Length", strconv.Itoa(len(b)))
totalSize := len(w.Response.Proto) + 1 + len(strconv.Itoa(w.Response.StatusCode)) + 1 + len(w.Response.Status) + 2
for key, values := range w.Response.Header {
for _, value := range values {
totalSize += len(key) + 2 + len(value) + 2
}
}
totalSize += 2
totalSize += len(b)
response := make([]byte, 0, totalSize)
response = append(response, w.Response.Proto...)
response = append(response, ' ')
response = append(response, strconv.Itoa(w.Response.StatusCode)...)
response = append(response, ' ')
response = append(response, w.Response.Status...)
response = append(response, '\r', '\n')
for key, values := range w.Response.Header {
for _, value := range values {
response = append(response, key...)
response = append(response, ':', ' ')
response = append(response, value...)
response = append(response, '\r', '\n')
}
}
response = append(response, '\r', '\n')
response = append(response, b...)
w.Response.Body = io.NopCloser(bytes.NewReader(b))
return w.conn.Write(response)
}
func (w *responseWriter) WriteHeader(statusCode int) {
w.Response.StatusCode = statusCode
w.Response.Status = http.StatusText(statusCode)
}