cmds/http/handler: set stream error trailer
stream output might break. in these cases we need to notify the client. this is after a 200 response has been sent. We do this by setting a special trailer (header after the body): X-Stream-Error: <error cause> This is similar to what's done by systems like gRPC. This still needs to be read + handled on the other side. License: MIT Signed-off-by: Juan Batiz-Benet <juan@benet.ai>
Juan Batiz-Benet committed
Jul 24, 2015 at 14:45 UTC
814f437fb48b26b4e4cede8a1bd102fe66b4edef
1 file changed
+37
-20
commands/http/handler.go
+37
-20
@@ -32,6 +32,7 @@ type Handler struct {
32
var ErrNotFound = errors.New("404 page not found")
33
34
const (
35
+ StreamErrHeader = "X-Stream-Error"
36
streamHeader = "X-Stream-Output"
37
channelHeader = "X-Chunked-Output"
38
contentTypeHeader = "Content-Type"
@@ -213,38 +214,54 @@ func copyChunks(contentType string, w http.ResponseWriter, out io.Reader) error
214
writer.WriteString(transferEncodingHeader + ": chunked\r\n")
215
writer.WriteString(channelHeader + ": 1\r\n\r\n")
216
216
- buf := make([]byte, 32*1024)
217
+ writeChunks := func() error {
218
+ buf := make([]byte, 32*1024)
219
+ for {
220
+ n, err := out.Read(buf)
221
218
- for {
219
- n, err := out.Read(buf)
222
+ if n > 0 {
223
+ length := fmt.Sprintf("%x\r\n", n)
224
+ writer.WriteString(length)
225
221
- if n > 0 {
222
- length := fmt.Sprintf("%x\r\n", n)
223
- writer.WriteString(length)
226
+ _, err := writer.Write(buf[0:n])
227
+ if err != nil {
228
+ return err
229
+ }
230
225
- _, err := writer.Write(buf[0:n])
226
- if err != nil {
227
- return err
231
+ writer.WriteString("\r\n")
232
+ writer.Flush()
233
}
234
230
- writer.WriteString("\r\n")
231
- writer.Flush()
232
- }
233
-
234
- if err != nil && err != io.EOF {
235
- return err
236
- }
237
- if err == io.EOF {
238
- break
235
+ if err != nil && err != io.EOF {
236
+ return err
237
+ }
238
+ if err == io.EOF {
239
+ break
240
+ }
241
}
242
+ return nil
243
}
244
242
- writer.WriteString("0\r\n\r\n")
243
- writer.Flush()
245
+ streamErr := writeChunks()
246
+ writer.WriteString("0\r\n") // close body
247
248
+ // if there was a stream error, write out an error trailer. hopefully
249
+ // the client will pick it up!
250
+ if streamErr != nil {
251
+ writer.WriteString(StreamErrHeader + ": " + sanitizedErrStr(err) + "\r\n")
252
+ }
253
+ writer.WriteString("\r\n") // close response
254
+ writer.Flush()
255
return nil
256
}
257
258
+func sanitizedErrStr(err error) string {
259
+ s := err.Error()
260
+ s = strings.Split(s, "\n")[0]
261
+ s = strings.Split(s, "\r")[0]
262
+ return s
263
+}
264
+
265
type flushResponse struct {
266
W http.ResponseWriter
267
}