fix(http): keep the http response body open until the result has been read

This modifies the `MultiResultDecoder` interface to accept an
`io.ReadCloser` so the `ResultIterator` can close the `io.Reader`
instead of doing it through a defer call. It then makes it so the
`Cancel()` method will close the reader or the reader will be
automatically closed when `More()` returns false.
pull/10616/head
Jonathan A. Sternberg 2018-05-25 15:03:52 -05:00
parent 65c510ec3b
commit 911f053dd6
6 changed files with 23 additions and 9 deletions

View File

@ -156,7 +156,6 @@ func (s *QueryService) Query(ctx context.Context, orgID platform.ID, query *ifql
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close()
return s.processResponse(resp) return s.processResponse(resp)
} }
@ -182,7 +181,6 @@ func (s *QueryService) QueryWithCompile(ctx context.Context, orgID platform.ID,
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close()
return s.processResponse(resp) return s.processResponse(resp)
} }

View File

@ -75,7 +75,7 @@ func (d *ResultDecoder) Decode(r io.Reader) (query.Result, error) {
// Results are delimited by an empty line. // Results are delimited by an empty line.
type MultiResultDecoder struct { type MultiResultDecoder struct {
c ResultDecoderConfig c ResultDecoderConfig
r io.Reader r io.ReadCloser
} }
// NewMultiResultDecoder creates a new MultiResultDecoder. // NewMultiResultDecoder creates a new MultiResultDecoder.
@ -88,7 +88,7 @@ func NewMultiResultDecoder(c ResultDecoderConfig) *MultiResultDecoder {
} }
} }
func (d *MultiResultDecoder) Decode(r io.Reader) (query.ResultIterator, error) { func (d *MultiResultDecoder) Decode(r io.ReadCloser) (query.ResultIterator, error) {
return &resultIterator{ return &resultIterator{
c: d.c, c: d.c,
r: r, r: r,
@ -98,9 +98,11 @@ func (d *MultiResultDecoder) Decode(r io.Reader) (query.ResultIterator, error) {
// resultIterator iterates through the results encoded in r. // resultIterator iterates through the results encoded in r.
type resultIterator struct { type resultIterator struct {
c ResultDecoderConfig c ResultDecoderConfig
r io.Reader r io.ReadCloser
next *resultDecoder next *resultDecoder
err error err error
canceled bool
} }
func (r *resultIterator) More() bool { func (r *resultIterator) More() bool {
@ -110,8 +112,13 @@ func (r *resultIterator) More() bool {
extraMeta = r.next.extraMeta extraMeta = r.next.extraMeta
} }
r.next, r.err = newResultDecoder(r.r, r.c, extraMeta) r.next, r.err = newResultDecoder(r.r, r.c, extraMeta)
return r.err == nil if r.err == nil {
return true
} }
}
// Release the resources for this query.
r.Cancel()
return false return false
} }
@ -120,6 +127,14 @@ func (r *resultIterator) Next() query.Result {
} }
func (r *resultIterator) Cancel() { func (r *resultIterator) Cancel() {
if r.canceled {
return
}
if err := r.r.Close(); err != nil && r.err == nil {
r.err = err
}
r.canceled = true
} }
func (r *resultIterator) Err() error { func (r *resultIterator) Err() error {

View File

@ -139,7 +139,7 @@ func (e *MultiResultEncoder) Encode(w io.Writer, results query.ResultIterator) e
resp.Results = append(resp.Results, result) resp.Results = append(resp.Results, result)
} }
if err := results.Err(); err != nil { if err := results.Err(); err != nil && resp.Err == "" {
resp.error(err) resp.error(err)
} }

View File

@ -198,6 +198,7 @@ func (r *SliceResultIterator) Next() Result {
} }
func (r *SliceResultIterator) Cancel() { func (r *SliceResultIterator) Cancel() {
r.results = nil
} }
func (r *SliceResultIterator) Err() error { func (r *SliceResultIterator) Err() error {

View File

@ -124,7 +124,7 @@ type ResultEncoder interface {
// MultiResultDecoder can decode multiple results from a reader. // MultiResultDecoder can decode multiple results from a reader.
type MultiResultDecoder interface { type MultiResultDecoder interface {
// Decode decodes multiple results from r. // Decode decodes multiple results from r.
Decode(r io.Reader) (ResultIterator, error) Decode(r io.ReadCloser) (ResultIterator, error)
} }
// MultiResultEncoder can encode multiple results into a writer. // MultiResultEncoder can encode multiple results into a writer.