From e89f7b8ded9b56d14fca5a1dc2906b8ecbe02a8b Mon Sep 17 00:00:00 2001 From: Jeromy Date: Fri, 28 Aug 2015 13:02:25 -0700 Subject: [PATCH] use go's built in handling of trailers and dont do custom chunking License: MIT Signed-off-by: Jeromy use go1.5 syntax to ensure builds on older versions fail License: MIT Signed-off-by: Jeromy fix t0230 License: MIT Signed-off-by: Jeromy --- .travis.yml | 3 +- cmd/ipfs/go_req.go | 3 + commands/http/handler.go | 83 ++----------------- ...230-channel-streaming-http-content-type.sh | 11 ++- 4 files changed, 21 insertions(+), 79 deletions(-) create mode 100644 cmd/ipfs/go_req.go diff --git a/.travis.yml b/.travis.yml index 79c4b4fe1..3979f557b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,8 +7,7 @@ os: language: go go: -# - 1.3 - - 1.4 + - 1.5.1 env: - TEST_NO_FUSE=1 TEST_VERBOSE=1 TEST_SUITE=test_go_expensive diff --git a/cmd/ipfs/go_req.go b/cmd/ipfs/go_req.go new file mode 100644 index 000000000..2d5929c63 --- /dev/null +++ b/cmd/ipfs/go_req.go @@ -0,0 +1,3 @@ +// +build !go1.5 + +`IPFS needs to be built with go version 1.5 or greater` diff --git a/commands/http/handler.go b/commands/http/handler.go index 86be6c2a4..c77d1a775 100644 --- a/commands/http/handler.go +++ b/commands/http/handler.go @@ -1,7 +1,6 @@ package http import ( - "bufio" "errors" "fmt" "io" @@ -205,6 +204,10 @@ func sendResponse(w http.ResponseWriter, r *http.Request, res cmds.Response, req } h := w.Header() + + // Set up our potential trailer + h.Set("Trailer", StreamErrHeader) + if res.Length() > 0 { h.Set(contentLengthHeader, strconv.FormatUint(res.Length(), 10)) } @@ -237,82 +240,12 @@ func sendResponse(w http.ResponseWriter, r *http.Request, res cmds.Response, req return } - if err := writeResponse(status, w, out); err != nil { - if strings.Contains(err.Error(), "broken pipe") { - log.Info("client disconnect while writing stream ", err) - return - } - - log.Error("error while writing stream ", err) - } -} - -// Copies from an io.Reader to a http.ResponseWriter. -// Flushes chunks over HTTP stream as they are read (if supported by transport). -func writeResponse(status int, w http.ResponseWriter, out io.Reader) error { - // hijack the connection so we can write our own chunked output and trailers - hijacker, ok := w.(http.Hijacker) - if !ok { - log.Error("Failed to create hijacker! cannot continue!") - return errors.New("Could not create hijacker") - } - conn, writer, err := hijacker.Hijack() + w.WriteHeader(status) + _, err = io.Copy(w, out) if err != nil { - return err + log.Error("err: ", err) + w.Header().Set(StreamErrHeader, sanitizedErrStr(err)) } - defer conn.Close() - - // write status - writer.WriteString(fmt.Sprintf("HTTP/1.1 %d %s\r\n", status, http.StatusText(status))) - - // Write out headers - w.Header().Write(writer) - - // end of headers - writer.WriteString("\r\n") - - // write body - streamErr := writeChunks(out, writer) - - // close body - writer.WriteString("0\r\n") - - // if there was a stream error, write out an error trailer. hopefully - // the client will pick it up! - if streamErr != nil { - writer.WriteString(StreamErrHeader + ": " + sanitizedErrStr(streamErr) + "\r\n") - } - writer.WriteString("\r\n") // close response - writer.Flush() - return streamErr -} - -func writeChunks(r io.Reader, w *bufio.ReadWriter) error { - buf := make([]byte, 32*1024) - for { - n, err := r.Read(buf) - - if n > 0 { - length := fmt.Sprintf("%x\r\n", n) - w.WriteString(length) - - _, err := w.Write(buf[0:n]) - if err != nil { - return err - } - - w.WriteString("\r\n") - w.Flush() - } - - if err != nil && err != io.EOF { - return err - } - if err == io.EOF { - break - } - } - return nil } func sanitizedErrStr(err error) string { diff --git a/test/sharness/t0230-channel-streaming-http-content-type.sh b/test/sharness/t0230-channel-streaming-http-content-type.sh index b230b0395..f4dc81c26 100755 --- a/test/sharness/t0230-channel-streaming-http-content-type.sh +++ b/test/sharness/t0230-channel-streaming-http-content-type.sh @@ -22,11 +22,14 @@ test_ls_cmd() { test_expect_success "Text encoded channel-streaming command output looks good" ' printf "HTTP/1.1 200 OK\r\n" >expected_output && printf "Content-Type: text/plain\r\n" >>expected_output && + printf "Trailer: X-Stream-Error\r\n" >>expected_output && printf "Transfer-Encoding: chunked\r\n" >>expected_output && printf "X-Chunked-Output: 1\r\n" >>expected_output && + printf "Transfer-Encoding: chunked\r\n" >>expected_output && printf "\r\n" >>expected_output && echo QmRmPLc1FsPAn8F8F9DQDEYADNX5ER2sgqiokEvqnYknVW >>expected_output && - test_cmp expected_output actual_output + cat actual_output | grep -vE Date > cleaned_output && + test_cmp expected_output cleaned_output ' test_expect_success "JSON encoded channel-streaming command succeeds" ' @@ -39,8 +42,10 @@ test_ls_cmd() { test_expect_success "JSON encoded channel-streaming command output looks good" ' printf "HTTP/1.1 200 OK\r\n" >expected_output && printf "Content-Type: application/json\r\n" >>expected_output && + printf "Trailer: X-Stream-Error\r\n" >>expected_output && printf "Transfer-Encoding: chunked\r\n" >>expected_output && printf "X-Chunked-Output: 1\r\n" >>expected_output && + printf "Transfer-Encoding: chunked\r\n" >>expected_output && printf "\r\n" >>expected_output && cat <<-\EOF >>expected_output && { @@ -48,8 +53,10 @@ test_ls_cmd() { "Err": "" } EOF + printf "\n" >> expected_output && perl -pi -e '"'"'chomp if eof'"'"' expected_output && - test_cmp expected_output actual_output + cat actual_output | grep -vE Date > cleaned_output && + test_cmp expected_output cleaned_output ' }