github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/net/context/ctxhttp/ctxhttp.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package ctxhttp provides helper functions for performing context-aware HTTP requests.
     6  package ctxhttp // import "golang.org/x/net/context/ctxhttp"
     7  
     8  import (
     9  	"io"
    10  	"net/http"
    11  	"net/url"
    12  	"strings"
    13  
    14  	"golang.org/x/net/context"
    15  )
    16  
    17  // Do sends an HTTP request with the provided http.Client and returns an HTTP response.
    18  // If the client is nil, http.DefaultClient is used.
    19  // If the context is canceled or times out, ctx.Err() will be returned.
    20  func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
    21  	if client == nil {
    22  		client = http.DefaultClient
    23  	}
    24  
    25  	// Request cancelation changed in Go 1.5, see cancelreq.go and cancelreq_go14.go.
    26  	cancel := canceler(client, req)
    27  
    28  	type responseAndError struct {
    29  		resp *http.Response
    30  		err  error
    31  	}
    32  	result := make(chan responseAndError, 1)
    33  
    34  	go func() {
    35  		resp, err := client.Do(req)
    36  		result <- responseAndError{resp, err}
    37  	}()
    38  
    39  	var resp *http.Response
    40  
    41  	select {
    42  	case <-ctx.Done():
    43  		cancel()
    44  		return nil, ctx.Err()
    45  	case r := <-result:
    46  		var err error
    47  		resp, err = r.resp, r.err
    48  		if err != nil {
    49  			return resp, err
    50  		}
    51  	}
    52  
    53  	c := make(chan struct{})
    54  	go func() {
    55  		select {
    56  		case <-ctx.Done():
    57  			cancel()
    58  		case <-c:
    59  			// The response's Body is closed.
    60  		}
    61  	}()
    62  	resp.Body = &notifyingReader{resp.Body, c}
    63  
    64  	return resp, nil
    65  }
    66  
    67  // Get issues a GET request via the Do function.
    68  func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
    69  	req, err := http.NewRequest("GET", url, nil)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	return Do(ctx, client, req)
    74  }
    75  
    76  // Head issues a HEAD request via the Do function.
    77  func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
    78  	req, err := http.NewRequest("HEAD", url, nil)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	return Do(ctx, client, req)
    83  }
    84  
    85  // Post issues a POST request via the Do function.
    86  func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) {
    87  	req, err := http.NewRequest("POST", url, body)
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  	req.Header.Set("Content-Type", bodyType)
    92  	return Do(ctx, client, req)
    93  }
    94  
    95  // PostForm issues a POST request via the Do function.
    96  func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) {
    97  	return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
    98  }
    99  
   100  // notifyingReader is an io.ReadCloser that closes the notify channel after
   101  // Close is called or a Read fails on the underlying ReadCloser.
   102  type notifyingReader struct {
   103  	io.ReadCloser
   104  	notify chan<- struct{}
   105  }
   106  
   107  func (r *notifyingReader) Read(p []byte) (int, error) {
   108  	n, err := r.ReadCloser.Read(p)
   109  	if err != nil && r.notify != nil {
   110  		close(r.notify)
   111  		r.notify = nil
   112  	}
   113  	return n, err
   114  }
   115  
   116  func (r *notifyingReader) Close() error {
   117  	err := r.ReadCloser.Close()
   118  	if r.notify != nil {
   119  		close(r.notify)
   120  		r.notify = nil
   121  	}
   122  	return err
   123  }