@cryptotaxi247 / kubo / commits / 814f437fb

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 }