@cryptotaxi247 / kubo / commits / ddd754018

cmds: flush output on standard readers

cc @mappum can we do this for the copyChunks case?

Juan Batiz-Benet committed Jan 10, 2015 at 20:37 UTC ddd7540186598ddafa12951d4dcfc3b2aa4f2774
2 files changed +49 -5
commands/http/handler.go
+41 -4
@@ -6,6 +6,8 @@ import (
6 "io"
7 "net/http"
8
9 + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
10 +
11 cmds "github.com/jbenet/go-ipfs/commands"
12 u "github.com/jbenet/go-ipfs/util"
13 )
@@ -26,6 +28,7 @@ const (
28 contentTypeHeader = "Content-Type"
29 contentLengthHeader = "Content-Length"
30 transferEncodingHeader = "Transfer-Encoding"
31 + applicationJson = "application/json"
32 )
33
34 var mimeTypes = map[string]string{
@@ -44,6 +47,11 @@ func NewHandler(ctx cmds.Context, root *cmds.Command, origin string) *Handler {
47 }
48
49 func (i Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
50 + // create a context.Context to pass into the commands.
51 + ctx, cancel := context.WithCancel(context.TODO())
52 + defer cancel()
53 + i.ctx.Context = ctx
54 +
55 log.Debug("Incoming API request: ", r.URL)
56
57 if len(i.origin) > 0 {
@@ -106,19 +114,30 @@ func (i Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
114 _, isChan := res.Output().(chan interface{})
115 streamChans, _, _ := req.Option("stream-channels").Bool()
116 if isChan && streamChans {
109 - err = copyChunks(w, out)
117 + err = copyChunks(applicationJson, w, out)
118 if err != nil {
119 log.Error(err)
120 }
121 return
122 }
123
116 - io.Copy(w, out)
124 + flushCopy(w, out)
125 +}
126 +
127 +// flushCopy Copies from an io.Reader to a http.ResponseWriter.
128 +// Flushes chunks over HTTP stream as they are read (if supported by transport).
129 +func flushCopy(w http.ResponseWriter, out io.Reader) error {
130 + if _, ok := w.(http.Flusher); !ok {
131 + return copyChunks("", w, out)
132 + }
133 +
134 + io.Copy(&flushResponse{w}, out)
135 + return nil
136 }
137
138 // Copies from an io.Reader to a http.ResponseWriter.
139 // Flushes chunks over HTTP stream as they are read (if supported by transport).
121 -func copyChunks(w http.ResponseWriter, out io.Reader) error {
140 +func copyChunks(contentType string, w http.ResponseWriter, out io.Reader) error {
141 hijacker, ok := w.(http.Hijacker)
142 if !ok {
143 return errors.New("Could not create hijacker")
@@ -130,7 +149,9 @@ func copyChunks(w http.ResponseWriter, out io.Reader) error {
149 defer conn.Close()
150
151 writer.WriteString("HTTP/1.1 200 OK\r\n")
133 - writer.WriteString(contentTypeHeader + ": application/json\r\n")
152 + if contentType != "" {
153 + writer.WriteString(contentTypeHeader + ": " + contentType + "\r\n")
154 + }
155 writer.WriteString(transferEncodingHeader + ": chunked\r\n")
156 writer.WriteString(channelHeader + ": 1\r\n\r\n")
157
@@ -165,3 +186,19 @@ func copyChunks(w http.ResponseWriter, out io.Reader) error {
186
187 return nil
188 }
189 +
190 +type flushResponse struct {
191 + W http.ResponseWriter
192 +}
193 +
194 +func (fr *flushResponse) Write(buf []byte) (int, error) {
195 + n, err := fr.W.Write(buf)
196 + if err != nil {
197 + return n, err
198 + }
199 +
200 + if flusher, ok := fr.W.(http.Flusher); ok {
201 + flusher.Flush()
202 + }
203 + return n, err
204 +}
commands/request.go
+8 -1
@@ -6,6 +6,8 @@ import (
6 "reflect"
7 "strconv"
8
9 + context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
10 +
11 "github.com/jbenet/go-ipfs/config"
12 "github.com/jbenet/go-ipfs/core"
13 u "github.com/jbenet/go-ipfs/util"
@@ -14,6 +16,10 @@ import (
16 type optMap map[string]interface{}
17
18 type Context struct {
19 + // this Context is temporary. Will be replaced soon, as we get
20 + // rid of this variable entirely.
21 + Context context.Context
22 +
23 Online bool
24 ConfigRoot string
25
@@ -267,7 +273,8 @@ func NewRequest(path []string, opts optMap, args []string, file File, cmd *Comma
273 optDefs = make(map[string]Option)
274 }
275
270 - req := &request{path, opts, args, file, cmd, Context{}, optDefs}
276 + ctx := Context{Context: context.TODO()}
277 + req := &request{path, opts, args, file, cmd, ctx, optDefs}
278 err := req.ConvertOptions()
279 if err != nil {
280 return nil, err