address comments from CR and fix random failures
License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>
Jeromy committed
Jul 27, 2015 at 09:30 UTC
23d41e082399ec5386f691ce907d38e767f54dff
2 files changed
+29
-19
commands/http/client.go
+24
-18
@@ -30,10 +30,18 @@ type Client interface {
30
31
type client struct {
32
serverAddress string
33
+ httpClient http.Client
34
}
35
36
func NewClient(address string) Client {
36
- return &client{address}
37
+ return &client{
38
+ serverAddress: address,
39
+ httpClient: http.Client{
40
+ Transport: &http.Transport{
41
+ DisableKeepAlives: true,
42
+ },
43
+ },
44
+ }
45
}
46
47
func (c *client) Send(req cmds.Request) (cmds.Response, error) {
@@ -85,20 +93,20 @@ func (c *client) Send(req cmds.Request) (cmds.Response, error) {
93
94
// TODO extract string consts?
95
if fileReader != nil {
88
- httpReq.Header.Set("Content-Type", "multipart/form-data; boundary="+fileReader.Boundary())
89
- httpReq.Header.Set("Content-Disposition", "form-data: name=\"files\"")
96
+ httpReq.Header.Set(contentTypeHeader, "multipart/form-data; boundary="+fileReader.Boundary())
97
+ httpReq.Header.Set(contentDispHeader, "form-data: name=\"files\"")
98
} else {
91
- httpReq.Header.Set("Content-Type", "application/octet-stream")
99
+ httpReq.Header.Set(contentTypeHeader, applicationOctetStream)
100
}
101
version := config.CurrentVersionNumber
94
- httpReq.Header.Set("User-Agent", fmt.Sprintf("/go-ipfs/%s/", version))
102
+ httpReq.Header.Set(uaHeader, fmt.Sprintf("/go-ipfs/%s/", version))
103
104
ec := make(chan error, 1)
105
rc := make(chan cmds.Response, 1)
106
dc := req.Context().Done()
107
108
go func() {
101
- httpRes, err := http.DefaultClient.Do(httpReq)
109
+ httpRes, err := c.httpClient.Do(httpReq)
110
if err != nil {
111
ec <- err
112
return
@@ -182,24 +190,25 @@ func getResponse(httpRes *http.Response, req cmds.Request) (cmds.Response, error
190
res.SetLength(length)
191
}
192
185
- res.SetCloser(httpRes.Body)
193
+ rr := &httpResponseReader{httpRes}
194
+ res.SetCloser(rr)
195
187
- if contentType != "application/json" {
196
+ if contentType != applicationJson {
197
// for all non json output types, just stream back the output
189
- res.SetOutput(&httpResponseReader{httpRes})
198
+ res.SetOutput(rr)
199
return res, nil
200
201
} else if len(httpRes.Header.Get(channelHeader)) > 0 {
202
// if output is coming from a channel, decode each chunk
203
outChan := make(chan interface{})
204
196
- go readStreamedJson(req, httpRes, outChan)
205
+ go readStreamedJson(req, rr, outChan)
206
207
res.SetOutput((<-chan interface{})(outChan))
208
return res, nil
209
}
210
202
- dec := json.NewDecoder(&httpResponseReader{httpRes})
211
+ dec := json.NewDecoder(rr)
212
213
// If we ran into an error
214
if httpRes.StatusCode >= http.StatusBadRequest {
@@ -211,7 +220,7 @@ func getResponse(httpRes *http.Response, req cmds.Request) (cmds.Response, error
220
e.Message = "Command not found."
221
e.Code = cmds.ErrClient
222
214
- case contentType == "text/plain":
223
+ case contentType == plainText:
224
// handle non-marshalled errors
225
buf := bytes.NewBuffer(nil)
226
io.Copy(buf, httpRes.Body)
@@ -244,9 +253,9 @@ func getResponse(httpRes *http.Response, req cmds.Request) (cmds.Response, error
253
254
// read json objects off of the given stream, and write the objects out to
255
// the 'out' channel
247
-func readStreamedJson(req cmds.Request, httpRes *http.Response, out chan<- interface{}) {
256
+func readStreamedJson(req cmds.Request, rr io.Reader, out chan<- interface{}) {
257
defer close(out)
249
- dec := json.NewDecoder(&httpResponseReader{httpRes})
258
+ dec := json.NewDecoder(rr)
259
outputType := reflect.TypeOf(req.Command().Type)
260
261
ctx := req.Context()
@@ -254,9 +263,7 @@ func readStreamedJson(req cmds.Request, httpRes *http.Response, out chan<- inter
263
for {
264
v, err := decodeTypedVal(outputType, dec)
265
if err != nil {
257
- // since we are just looping reading on the response, the only way to
258
- // know we are 'done' is for the consumer to close the response body.
259
- // doing so doesnt throw an io.EOF, but we want to treat it like one.
266
+ // reading on a closed response body is as good as an io.EOF here
267
if !(strings.Contains(err.Error(), "read on closed response body") || err == io.EOF) {
268
log.Error(err)
269
}
@@ -268,7 +275,6 @@ func readStreamedJson(req cmds.Request, httpRes *http.Response, out chan<- inter
275
return
276
case out <- v:
277
}
271
-
278
}
279
}
280
commands/http/handler.go
+5
-1
@@ -36,10 +36,14 @@ const (
36
StreamErrHeader = "X-Stream-Error"
37
streamHeader = "X-Stream-Output"
38
channelHeader = "X-Chunked-Output"
39
+ uaHeader = "User-Agent"
40
contentTypeHeader = "Content-Type"
41
contentLengthHeader = "Content-Length"
42
+ contentDispHeader = "Content-Disposition"
43
transferEncodingHeader = "Transfer-Encoding"
44
applicationJson = "application/json"
45
+ applicationOctetStream = "application/octet-stream"
46
+ plainText = "text/plain"
47
)
48
49
var mimeTypes = map[string]string{
@@ -156,7 +160,7 @@ func sendResponse(w http.ResponseWriter, req cmds.Request, res cmds.Response) {
160
return
161
}
162
159
- status := 200
163
+ status := http.StatusOK
164
// if response contains an error, write an HTTP error status code
165
if e := res.Error(); e != nil {
166
if e.Code == cmds.ErrClient {