Skip to content

Commit 9318e62

Browse files
authored
Add client.BareDo to allow user to handle the body (google#1772)
1 parent 5f64fee commit 9318e62

File tree

2 files changed

+75
-26
lines changed

2 files changed

+75
-26
lines changed

github/github.go

+40-26
Original file line numberDiff line numberDiff line change
@@ -512,16 +512,15 @@ func parseRate(r *http.Response) Rate {
512512
return rate
513513
}
514514

515-
// Do sends an API request and returns the API response. The API response is
516-
// JSON decoded and stored in the value pointed to by v, or returned as an
517-
// error if an API error has occurred. If v implements the io.Writer
518-
// interface, the raw response body will be written to v, without attempting to
519-
// first decode it. If rate limit is exceeded and reset time is in the future,
520-
// Do returns *RateLimitError immediately without making a network API call.
515+
// BareDo sends an API request and lets you handle the api response. If an error
516+
// or API Error occurs, the error will contain more information. Otherwise you
517+
// are supposed to read and close the response's Body. If rate limit is exceeded
518+
// and reset time is in the future, BareDo returns *RateLimitError immediately
519+
// without making a network API call.
521520
//
522-
// The provided ctx must be non-nil, if it is nil an error is returned. If it is canceled or times out,
523-
// ctx.Err() will be returned.
524-
func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Response, error) {
521+
// The provided ctx must be non-nil, if it is nil an error is returned. If it is
522+
// canceled or times out, ctx.Err() will be returned.
523+
func (c *Client) BareDo(ctx context.Context, req *http.Request) (*Response, error) {
525524
if ctx == nil {
526525
return nil, errors.New("context must be non-nil")
527526
}
@@ -558,8 +557,6 @@ func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Res
558557
return nil, err
559558
}
560559

561-
defer resp.Body.Close()
562-
563560
response := newResponse(resp)
564561

565562
c.rateMu.Lock()
@@ -568,6 +565,7 @@ func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Res
568565

569566
err = CheckResponse(resp)
570567
if err != nil {
568+
defer resp.Body.Close()
571569
// Special case for AcceptedErrors. If an AcceptedError
572570
// has been encountered, the response's payload will be
573571
// added to the AcceptedError and returned.
@@ -581,27 +579,43 @@ func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Res
581579
}
582580

583581
aerr.Raw = b
584-
return response, aerr
582+
err = aerr
585583
}
584+
}
585+
return response, err
586+
}
586587

587-
return response, err
588+
// Do sends an API request and returns the API response. The API response is
589+
// JSON decoded and stored in the value pointed to by v, or returned as an
590+
// error if an API error has occurred. If v implements the io.Writer interface,
591+
// the raw response body will be written to v, without attempting to first
592+
// decode it. If v is nil, and no error hapens, the response is returned as is.
593+
// If rate limit is exceeded and reset time is in the future, Do returns
594+
// *RateLimitError immediately without making a network API call.
595+
//
596+
// The provided ctx must be non-nil, if it is nil an error is returned. If it
597+
// is canceled or times out, ctx.Err() will be returned.
598+
func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Response, error) {
599+
resp, err := c.BareDo(ctx, req)
600+
if err != nil {
601+
return resp, err
588602
}
603+
defer resp.Body.Close()
589604

590-
if v != nil {
591-
if w, ok := v.(io.Writer); ok {
592-
io.Copy(w, resp.Body)
593-
} else {
594-
decErr := json.NewDecoder(resp.Body).Decode(v)
595-
if decErr == io.EOF {
596-
decErr = nil // ignore EOF errors caused by empty response body
597-
}
598-
if decErr != nil {
599-
err = decErr
600-
}
605+
switch v := v.(type) {
606+
case nil:
607+
case io.Writer:
608+
_, err = io.Copy(v, resp.Body)
609+
default:
610+
decErr := json.NewDecoder(resp.Body).Decode(v)
611+
if decErr == io.EOF {
612+
decErr = nil // ignore EOF errors caused by empty response body
613+
}
614+
if decErr != nil {
615+
err = decErr
601616
}
602617
}
603-
604-
return response, err
618+
return resp, err
605619
}
606620

607621
// checkRateLimitBeforeDo does not make any network calls, but uses existing knowledge from

github/github_test.go

+35
Original file line numberDiff line numberDiff line change
@@ -1560,3 +1560,38 @@ func TestAddOptions_QueryValues(t *testing.T) {
15601560
t.Error("addOptions err = nil, want error")
15611561
}
15621562
}
1563+
1564+
func TestBareDo_returnsOpenBody(t *testing.T) {
1565+
1566+
client, mux, _, teardown := setup()
1567+
defer teardown()
1568+
1569+
expectedBody := "Hello from the other side !"
1570+
1571+
mux.HandleFunc("/test-url", func(w http.ResponseWriter, r *http.Request) {
1572+
testMethod(t, r, "GET")
1573+
fmt.Fprint(w, expectedBody)
1574+
})
1575+
1576+
ctx := context.Background()
1577+
req, err := client.NewRequest("GET", "test-url", nil)
1578+
if err != nil {
1579+
t.Fatalf("client.NewRequest returned error: %v", err)
1580+
}
1581+
1582+
resp, err := client.BareDo(ctx, req)
1583+
if err != nil {
1584+
t.Fatalf("client.BareDo returned error: %v", err)
1585+
}
1586+
1587+
got, err := ioutil.ReadAll(resp.Body)
1588+
if err != nil {
1589+
t.Fatalf("ioutil.ReadAll returned error: %v", err)
1590+
}
1591+
if string(got) != expectedBody {
1592+
t.Fatalf("Expected %q, got %q", expectedBody, string(got))
1593+
}
1594+
if err := resp.Body.Close(); err != nil {
1595+
t.Fatalf("resp.Body.Close() returned error: %v", err)
1596+
}
1597+
}

0 commit comments

Comments
 (0)