aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/Azure/azure-pipeline-go/pipeline/request.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/Azure/azure-pipeline-go/pipeline/request.go')
-rwxr-xr-xvendor/github.com/Azure/azure-pipeline-go/pipeline/request.go147
1 files changed, 147 insertions, 0 deletions
diff --git a/vendor/github.com/Azure/azure-pipeline-go/pipeline/request.go b/vendor/github.com/Azure/azure-pipeline-go/pipeline/request.go
new file mode 100755
index 000000000..1fbe72bd4
--- /dev/null
+++ b/vendor/github.com/Azure/azure-pipeline-go/pipeline/request.go
@@ -0,0 +1,147 @@
+package pipeline
+
+import (
+ "io"
+ "net/http"
+ "net/url"
+ "strconv"
+)
+
+// Request is a thin wrapper over an http.Request. The wrapper provides several helper methods.
+type Request struct {
+ *http.Request
+}
+
+// NewRequest initializes a new HTTP request object with any desired options.
+func NewRequest(method string, url url.URL, body io.ReadSeeker) (request Request, err error) {
+ // Note: the url is passed by value so that any pipeline operations that modify it do so on a copy.
+
+ // This code to construct an http.Request is copied from http.NewRequest(); we intentionally omitted removeEmptyPort for now.
+ request.Request = &http.Request{
+ Method: method,
+ URL: &url,
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Header: make(http.Header),
+ Host: url.Host,
+ }
+
+ if body != nil {
+ err = request.SetBody(body)
+ }
+ return
+}
+
+// SetBody sets the body and content length, assumes body is not nil.
+func (r Request) SetBody(body io.ReadSeeker) error {
+ size, err := body.Seek(0, io.SeekEnd)
+ if err != nil {
+ return err
+ }
+
+ body.Seek(0, io.SeekStart)
+ r.ContentLength = size
+ r.Header["Content-Length"] = []string{strconv.FormatInt(size, 10)}
+
+ if size != 0 {
+ r.Body = &retryableRequestBody{body: body}
+ r.GetBody = func() (io.ReadCloser, error) {
+ _, err := body.Seek(0, io.SeekStart)
+ if err != nil {
+ return nil, err
+ }
+ return r.Body, nil
+ }
+ } else {
+ // in case the body is an empty stream, we need to use http.NoBody to explicitly provide no content
+ r.Body = http.NoBody
+ r.GetBody = func() (io.ReadCloser, error) {
+ return http.NoBody, nil
+ }
+
+ // close the user-provided empty body
+ if c, ok := body.(io.Closer); ok {
+ c.Close()
+ }
+ }
+
+ return nil
+}
+
+// Copy makes a copy of an http.Request. Specifically, it makes a deep copy
+// of its Method, URL, Host, Proto(Major/Minor), Header. ContentLength, Close,
+// RemoteAddr, RequestURI. Copy makes a shallow copy of the Body, GetBody, TLS,
+// Cancel, Response, and ctx fields. Copy panics if any of these fields are
+// not nil: TransferEncoding, Form, PostForm, MultipartForm, or Trailer.
+func (r Request) Copy() Request {
+ if r.TransferEncoding != nil || r.Form != nil || r.PostForm != nil || r.MultipartForm != nil || r.Trailer != nil {
+ panic("Can't make a deep copy of the http.Request because at least one of the following is not nil:" +
+ "TransferEncoding, Form, PostForm, MultipartForm, or Trailer.")
+ }
+ copy := *r.Request // Copy the request
+ urlCopy := *(r.Request.URL) // Copy the URL
+ copy.URL = &urlCopy
+ copy.Header = http.Header{} // Copy the header
+ for k, vs := range r.Header {
+ for _, value := range vs {
+ copy.Header.Add(k, value)
+ }
+ }
+ return Request{Request: &copy} // Return the copy
+}
+
+func (r Request) close() error {
+ if r.Body != nil && r.Body != http.NoBody {
+ c, ok := r.Body.(*retryableRequestBody)
+ if !ok {
+ panic("unexpected request body type (should be *retryableReadSeekerCloser)")
+ }
+ return c.realClose()
+ }
+ return nil
+}
+
+// RewindBody seeks the request's Body stream back to the beginning so it can be resent when retrying an operation.
+func (r Request) RewindBody() error {
+ if r.Body != nil && r.Body != http.NoBody {
+ s, ok := r.Body.(io.Seeker)
+ if !ok {
+ panic("unexpected request body type (should be io.Seeker)")
+ }
+
+ // Reset the stream back to the beginning
+ _, err := s.Seek(0, io.SeekStart)
+ return err
+ }
+ return nil
+}
+
+// ********** The following type/methods implement the retryableRequestBody (a ReadSeekCloser)
+
+// This struct is used when sending a body to the network
+type retryableRequestBody struct {
+ body io.ReadSeeker // Seeking is required to support retries
+}
+
+// Read reads a block of data from an inner stream and reports progress
+func (b *retryableRequestBody) Read(p []byte) (n int, err error) {
+ return b.body.Read(p)
+}
+
+func (b *retryableRequestBody) Seek(offset int64, whence int) (offsetFromStart int64, err error) {
+ return b.body.Seek(offset, whence)
+}
+
+func (b *retryableRequestBody) Close() error {
+ // We don't want the underlying transport to close the request body on transient failures so this is a nop.
+ // The pipeline closes the request body upon success.
+ return nil
+}
+
+func (b *retryableRequestBody) realClose() error {
+ if c, ok := b.body.(io.Closer); ok {
+ return c.Close()
+ }
+ return nil
+}