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