Blame vendor/github.com/Azure/go-autorest/autorest/azure/async.go

Packit 63bb0d
package azure
Packit 63bb0d
Packit 63bb0d
// Copyright 2017 Microsoft Corporation
Packit 63bb0d
//
Packit 63bb0d
//  Licensed under the Apache License, Version 2.0 (the "License");
Packit 63bb0d
//  you may not use this file except in compliance with the License.
Packit 63bb0d
//  You may obtain a copy of the License at
Packit 63bb0d
//
Packit 63bb0d
//      http://www.apache.org/licenses/LICENSE-2.0
Packit 63bb0d
//
Packit 63bb0d
//  Unless required by applicable law or agreed to in writing, software
Packit 63bb0d
//  distributed under the License is distributed on an "AS IS" BASIS,
Packit 63bb0d
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 63bb0d
//  See the License for the specific language governing permissions and
Packit 63bb0d
//  limitations under the License.
Packit 63bb0d
Packit 63bb0d
import (
Packit 63bb0d
	"bytes"
Packit 63bb0d
	"context"
Packit 63bb0d
	"encoding/json"
Packit 63bb0d
	"fmt"
Packit 63bb0d
	"io/ioutil"
Packit 63bb0d
	"net/http"
Packit 63bb0d
	"net/url"
Packit 63bb0d
	"strings"
Packit 63bb0d
	"time"
Packit 63bb0d
Packit 63bb0d
	"github.com/Azure/go-autorest/autorest"
Packit 63bb0d
	"github.com/Azure/go-autorest/tracing"
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
const (
Packit 63bb0d
	headerAsyncOperation = "Azure-AsyncOperation"
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
const (
Packit 63bb0d
	operationInProgress string = "InProgress"
Packit 63bb0d
	operationCanceled   string = "Canceled"
Packit 63bb0d
	operationFailed     string = "Failed"
Packit 63bb0d
	operationSucceeded  string = "Succeeded"
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
var pollingCodes = [...]int{http.StatusNoContent, http.StatusAccepted, http.StatusCreated, http.StatusOK}
Packit 63bb0d
Packit 63bb0d
// Future provides a mechanism to access the status and results of an asynchronous request.
Packit 63bb0d
// Since futures are stateful they should be passed by value to avoid race conditions.
Packit 63bb0d
type Future struct {
Packit 63bb0d
	pt pollingTracker
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// NewFutureFromResponse returns a new Future object initialized
Packit 63bb0d
// with the initial response from an asynchronous operation.
Packit 63bb0d
func NewFutureFromResponse(resp *http.Response) (Future, error) {
Packit 63bb0d
	pt, err := createPollingTracker(resp)
Packit 63bb0d
	return Future{pt: pt}, err
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Response returns the last HTTP response.
Packit 63bb0d
func (f Future) Response() *http.Response {
Packit 63bb0d
	if f.pt == nil {
Packit 63bb0d
		return nil
Packit 63bb0d
	}
Packit 63bb0d
	return f.pt.latestResponse()
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Status returns the last status message of the operation.
Packit 63bb0d
func (f Future) Status() string {
Packit 63bb0d
	if f.pt == nil {
Packit 63bb0d
		return ""
Packit 63bb0d
	}
Packit 63bb0d
	return f.pt.pollingStatus()
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// PollingMethod returns the method used to monitor the status of the asynchronous operation.
Packit 63bb0d
func (f Future) PollingMethod() PollingMethodType {
Packit 63bb0d
	if f.pt == nil {
Packit 63bb0d
		return PollingUnknown
Packit 63bb0d
	}
Packit 63bb0d
	return f.pt.pollingMethod()
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// DoneWithContext queries the service to see if the operation has completed.
Packit 63bb0d
func (f *Future) DoneWithContext(ctx context.Context, sender autorest.Sender) (done bool, err error) {
Packit 63bb0d
	ctx = tracing.StartSpan(ctx, "github.com/Azure/go-autorest/autorest/azure/async.DoneWithContext")
Packit 63bb0d
	defer func() {
Packit 63bb0d
		sc := -1
Packit 63bb0d
		resp := f.Response()
Packit 63bb0d
		if resp != nil {
Packit 63bb0d
			sc = resp.StatusCode
Packit 63bb0d
		}
Packit 63bb0d
		tracing.EndSpan(ctx, sc, err)
Packit 63bb0d
	}()
Packit 63bb0d
Packit 63bb0d
	if f.pt == nil {
Packit 63bb0d
		return false, autorest.NewError("Future", "Done", "future is not initialized")
Packit 63bb0d
	}
Packit 63bb0d
	if f.pt.hasTerminated() {
Packit 63bb0d
		return true, f.pt.pollingError()
Packit 63bb0d
	}
Packit 63bb0d
	if err := f.pt.pollForStatus(ctx, sender); err != nil {
Packit 63bb0d
		return false, err
Packit 63bb0d
	}
Packit 63bb0d
	if err := f.pt.checkForErrors(); err != nil {
Packit 63bb0d
		return f.pt.hasTerminated(), err
Packit 63bb0d
	}
Packit 63bb0d
	if err := f.pt.updatePollingState(f.pt.provisioningStateApplicable()); err != nil {
Packit 63bb0d
		return false, err
Packit 63bb0d
	}
Packit 63bb0d
	if err := f.pt.initPollingMethod(); err != nil {
Packit 63bb0d
		return false, err
Packit 63bb0d
	}
Packit 63bb0d
	if err := f.pt.updatePollingMethod(); err != nil {
Packit 63bb0d
		return false, err
Packit 63bb0d
	}
Packit 63bb0d
	return f.pt.hasTerminated(), f.pt.pollingError()
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// GetPollingDelay returns a duration the application should wait before checking
Packit 63bb0d
// the status of the asynchronous request and true; this value is returned from
Packit 63bb0d
// the service via the Retry-After response header.  If the header wasn't returned
Packit 63bb0d
// then the function returns the zero-value time.Duration and false.
Packit 63bb0d
func (f Future) GetPollingDelay() (time.Duration, bool) {
Packit 63bb0d
	if f.pt == nil {
Packit 63bb0d
		return 0, false
Packit 63bb0d
	}
Packit 63bb0d
	resp := f.pt.latestResponse()
Packit 63bb0d
	if resp == nil {
Packit 63bb0d
		return 0, false
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	retry := resp.Header.Get(autorest.HeaderRetryAfter)
Packit 63bb0d
	if retry == "" {
Packit 63bb0d
		return 0, false
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	d, err := time.ParseDuration(retry + "s")
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		panic(err)
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return d, true
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// WaitForCompletionRef will return when one of the following conditions is met: the long
Packit 63bb0d
// running operation has completed, the provided context is cancelled, or the client's
Packit 63bb0d
// polling duration has been exceeded.  It will retry failed polling attempts based on
Packit 63bb0d
// the retry value defined in the client up to the maximum retry attempts.
Packit 63bb0d
// If no deadline is specified in the context then the client.PollingDuration will be
Packit 63bb0d
// used to determine if a default deadline should be used.
Packit 63bb0d
// If PollingDuration is greater than zero the value will be used as the context's timeout.
Packit 63bb0d
// If PollingDuration is zero then no default deadline will be used.
Packit 63bb0d
func (f *Future) WaitForCompletionRef(ctx context.Context, client autorest.Client) (err error) {
Packit 63bb0d
	ctx = tracing.StartSpan(ctx, "github.com/Azure/go-autorest/autorest/azure/async.WaitForCompletionRef")
Packit 63bb0d
	defer func() {
Packit 63bb0d
		sc := -1
Packit 63bb0d
		resp := f.Response()
Packit 63bb0d
		if resp != nil {
Packit 63bb0d
			sc = resp.StatusCode
Packit 63bb0d
		}
Packit 63bb0d
		tracing.EndSpan(ctx, sc, err)
Packit 63bb0d
	}()
Packit 63bb0d
	cancelCtx := ctx
Packit 63bb0d
	// if the provided context already has a deadline don't override it
Packit 63bb0d
	_, hasDeadline := ctx.Deadline()
Packit 63bb0d
	if d := client.PollingDuration; !hasDeadline && d != 0 {
Packit 63bb0d
		var cancel context.CancelFunc
Packit 63bb0d
		cancelCtx, cancel = context.WithTimeout(ctx, d)
Packit 63bb0d
		defer cancel()
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	done, err := f.DoneWithContext(ctx, client)
Packit 63bb0d
	for attempts := 0; !done; done, err = f.DoneWithContext(ctx, client) {
Packit 63bb0d
		if attempts >= client.RetryAttempts {
Packit 63bb0d
			return autorest.NewErrorWithError(err, "Future", "WaitForCompletion", f.pt.latestResponse(), "the number of retries has been exceeded")
Packit 63bb0d
		}
Packit 63bb0d
		// we want delayAttempt to be zero in the non-error case so
Packit 63bb0d
		// that DelayForBackoff doesn't perform exponential back-off
Packit 63bb0d
		var delayAttempt int
Packit 63bb0d
		var delay time.Duration
Packit 63bb0d
		if err == nil {
Packit 63bb0d
			// check for Retry-After delay, if not present use the client's polling delay
Packit 63bb0d
			var ok bool
Packit 63bb0d
			delay, ok = f.GetPollingDelay()
Packit 63bb0d
			if !ok {
Packit 63bb0d
				delay = client.PollingDelay
Packit 63bb0d
			}
Packit 63bb0d
		} else {
Packit 63bb0d
			// there was an error polling for status so perform exponential
Packit 63bb0d
			// back-off based on the number of attempts using the client's retry
Packit 63bb0d
			// duration.  update attempts after delayAttempt to avoid off-by-one.
Packit 63bb0d
			delayAttempt = attempts
Packit 63bb0d
			delay = client.RetryDuration
Packit 63bb0d
			attempts++
Packit 63bb0d
		}
Packit 63bb0d
		// wait until the delay elapses or the context is cancelled
Packit 63bb0d
		delayElapsed := autorest.DelayForBackoff(delay, delayAttempt, cancelCtx.Done())
Packit 63bb0d
		if !delayElapsed {
Packit 63bb0d
			return autorest.NewErrorWithError(cancelCtx.Err(), "Future", "WaitForCompletion", f.pt.latestResponse(), "context has been cancelled")
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
	return
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// MarshalJSON implements the json.Marshaler interface.
Packit 63bb0d
func (f Future) MarshalJSON() ([]byte, error) {
Packit 63bb0d
	return json.Marshal(f.pt)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// UnmarshalJSON implements the json.Unmarshaler interface.
Packit 63bb0d
func (f *Future) UnmarshalJSON(data []byte) error {
Packit 63bb0d
	// unmarshal into JSON object to determine the tracker type
Packit 63bb0d
	obj := map[string]interface{}{}
Packit 63bb0d
	err := json.Unmarshal(data, &obj)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return err
Packit 63bb0d
	}
Packit 63bb0d
	if obj["method"] == nil {
Packit 63bb0d
		return autorest.NewError("Future", "UnmarshalJSON", "missing 'method' property")
Packit 63bb0d
	}
Packit 63bb0d
	method := obj["method"].(string)
Packit 63bb0d
	switch strings.ToUpper(method) {
Packit 63bb0d
	case http.MethodDelete:
Packit 63bb0d
		f.pt = &pollingTrackerDelete{}
Packit 63bb0d
	case http.MethodPatch:
Packit 63bb0d
		f.pt = &pollingTrackerPatch{}
Packit 63bb0d
	case http.MethodPost:
Packit 63bb0d
		f.pt = &pollingTrackerPost{}
Packit 63bb0d
	case http.MethodPut:
Packit 63bb0d
		f.pt = &pollingTrackerPut{}
Packit 63bb0d
	default:
Packit 63bb0d
		return autorest.NewError("Future", "UnmarshalJSON", "unsupoorted method '%s'", method)
Packit 63bb0d
	}
Packit 63bb0d
	// now unmarshal into the tracker
Packit 63bb0d
	return json.Unmarshal(data, &f.pt)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// PollingURL returns the URL used for retrieving the status of the long-running operation.
Packit 63bb0d
func (f Future) PollingURL() string {
Packit 63bb0d
	if f.pt == nil {
Packit 63bb0d
		return ""
Packit 63bb0d
	}
Packit 63bb0d
	return f.pt.pollingURL()
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// GetResult should be called once polling has completed successfully.
Packit 63bb0d
// It makes the final GET call to retrieve the resultant payload.
Packit 63bb0d
func (f Future) GetResult(sender autorest.Sender) (*http.Response, error) {
Packit 63bb0d
	if f.pt.finalGetURL() == "" {
Packit 63bb0d
		// we can end up in this situation if the async operation returns a 200
Packit 63bb0d
		// with no polling URLs.  in that case return the response which should
Packit 63bb0d
		// contain the JSON payload (only do this for successful terminal cases).
Packit 63bb0d
		if lr := f.pt.latestResponse(); lr != nil && f.pt.hasSucceeded() {
Packit 63bb0d
			return lr, nil
Packit 63bb0d
		}
Packit 63bb0d
		return nil, autorest.NewError("Future", "GetResult", "missing URL for retrieving result")
Packit 63bb0d
	}
Packit 63bb0d
	req, err := http.NewRequest(http.MethodGet, f.pt.finalGetURL(), nil)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return nil, err
Packit 63bb0d
	}
Packit 63bb0d
	resp, err := sender.Do(req)
Packit 63bb0d
	if err == nil && resp.Body != nil {
Packit 63bb0d
		// copy the body and close it so callers don't have to
Packit 63bb0d
		defer resp.Body.Close()
Packit 63bb0d
		b, err := ioutil.ReadAll(resp.Body)
Packit 63bb0d
		if err != nil {
Packit 63bb0d
			return resp, err
Packit 63bb0d
		}
Packit 63bb0d
		resp.Body = ioutil.NopCloser(bytes.NewReader(b))
Packit 63bb0d
	}
Packit 63bb0d
	return resp, err
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
type pollingTracker interface {
Packit 63bb0d
	// these methods can differ per tracker
Packit 63bb0d
Packit 63bb0d
	// checks the response headers and status code to determine the polling mechanism
Packit 63bb0d
	updatePollingMethod() error
Packit 63bb0d
Packit 63bb0d
	// checks the response for tracker-specific error conditions
Packit 63bb0d
	checkForErrors() error
Packit 63bb0d
Packit 63bb0d
	// returns true if provisioning state should be checked
Packit 63bb0d
	provisioningStateApplicable() bool
Packit 63bb0d
Packit 63bb0d
	// methods common to all trackers
Packit 63bb0d
Packit 63bb0d
	// initializes a tracker's polling URL and method, called for each iteration.
Packit 63bb0d
	// these values can be overridden by each polling tracker as required.
Packit 63bb0d
	initPollingMethod() error
Packit 63bb0d
Packit 63bb0d
	// initializes the tracker's internal state, call this when the tracker is created
Packit 63bb0d
	initializeState() error
Packit 63bb0d
Packit 63bb0d
	// makes an HTTP request to check the status of the LRO
Packit 63bb0d
	pollForStatus(ctx context.Context, sender autorest.Sender) error
Packit 63bb0d
Packit 63bb0d
	// updates internal tracker state, call this after each call to pollForStatus
Packit 63bb0d
	updatePollingState(provStateApl bool) error
Packit 63bb0d
Packit 63bb0d
	// returns the error response from the service, can be nil
Packit 63bb0d
	pollingError() error
Packit 63bb0d
Packit 63bb0d
	// returns the polling method being used
Packit 63bb0d
	pollingMethod() PollingMethodType
Packit 63bb0d
Packit 63bb0d
	// returns the state of the LRO as returned from the service
Packit 63bb0d
	pollingStatus() string
Packit 63bb0d
Packit 63bb0d
	// returns the URL used for polling status
Packit 63bb0d
	pollingURL() string
Packit 63bb0d
Packit 63bb0d
	// returns the URL used for the final GET to retrieve the resource
Packit 63bb0d
	finalGetURL() string
Packit 63bb0d
Packit 63bb0d
	// returns true if the LRO is in a terminal state
Packit 63bb0d
	hasTerminated() bool
Packit 63bb0d
Packit 63bb0d
	// returns true if the LRO is in a failed terminal state
Packit 63bb0d
	hasFailed() bool
Packit 63bb0d
Packit 63bb0d
	// returns true if the LRO is in a successful terminal state
Packit 63bb0d
	hasSucceeded() bool
Packit 63bb0d
Packit 63bb0d
	// returns the cached HTTP response after a call to pollForStatus(), can be nil
Packit 63bb0d
	latestResponse() *http.Response
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
type pollingTrackerBase struct {
Packit 63bb0d
	// resp is the last response, either from the submission of the LRO or from polling
Packit 63bb0d
	resp *http.Response
Packit 63bb0d
Packit 63bb0d
	// method is the HTTP verb, this is needed for deserialization
Packit 63bb0d
	Method string `json:"method"`
Packit 63bb0d
Packit 63bb0d
	// rawBody is the raw JSON response body
Packit 63bb0d
	rawBody map[string]interface{}
Packit 63bb0d
Packit 63bb0d
	// denotes if polling is using async-operation or location header
Packit 63bb0d
	Pm PollingMethodType `json:"pollingMethod"`
Packit 63bb0d
Packit 63bb0d
	// the URL to poll for status
Packit 63bb0d
	URI string `json:"pollingURI"`
Packit 63bb0d
Packit 63bb0d
	// the state of the LRO as returned from the service
Packit 63bb0d
	State string `json:"lroState"`
Packit 63bb0d
Packit 63bb0d
	// the URL to GET for the final result
Packit 63bb0d
	FinalGetURI string `json:"resultURI"`
Packit 63bb0d
Packit 63bb0d
	// used to hold an error object returned from the service
Packit 63bb0d
	Err *ServiceError `json:"error,omitempty"`
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt *pollingTrackerBase) initializeState() error {
Packit 63bb0d
	// determine the initial polling state based on response body and/or HTTP status
Packit 63bb0d
	// code.  this is applicable to the initial LRO response, not polling responses!
Packit 63bb0d
	pt.Method = pt.resp.Request.Method
Packit 63bb0d
	if err := pt.updateRawBody(); err != nil {
Packit 63bb0d
		return err
Packit 63bb0d
	}
Packit 63bb0d
	switch pt.resp.StatusCode {
Packit 63bb0d
	case http.StatusOK:
Packit 63bb0d
		if ps := pt.getProvisioningState(); ps != nil {
Packit 63bb0d
			pt.State = *ps
Packit 63bb0d
			if pt.hasFailed() {
Packit 63bb0d
				pt.updateErrorFromResponse()
Packit 63bb0d
				return pt.pollingError()
Packit 63bb0d
			}
Packit 63bb0d
		} else {
Packit 63bb0d
			pt.State = operationSucceeded
Packit 63bb0d
		}
Packit 63bb0d
	case http.StatusCreated:
Packit 63bb0d
		if ps := pt.getProvisioningState(); ps != nil {
Packit 63bb0d
			pt.State = *ps
Packit 63bb0d
		} else {
Packit 63bb0d
			pt.State = operationInProgress
Packit 63bb0d
		}
Packit 63bb0d
	case http.StatusAccepted:
Packit 63bb0d
		pt.State = operationInProgress
Packit 63bb0d
	case http.StatusNoContent:
Packit 63bb0d
		pt.State = operationSucceeded
Packit 63bb0d
	default:
Packit 63bb0d
		pt.State = operationFailed
Packit 63bb0d
		pt.updateErrorFromResponse()
Packit 63bb0d
		return pt.pollingError()
Packit 63bb0d
	}
Packit 63bb0d
	return pt.initPollingMethod()
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt pollingTrackerBase) getProvisioningState() *string {
Packit 63bb0d
	if pt.rawBody != nil && pt.rawBody["properties"] != nil {
Packit 63bb0d
		p := pt.rawBody["properties"].(map[string]interface{})
Packit 63bb0d
		if ps := p["provisioningState"]; ps != nil {
Packit 63bb0d
			s := ps.(string)
Packit 63bb0d
			return &s
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt *pollingTrackerBase) updateRawBody() error {
Packit 63bb0d
	pt.rawBody = map[string]interface{}{}
Packit 63bb0d
	if pt.resp.ContentLength != 0 {
Packit 63bb0d
		defer pt.resp.Body.Close()
Packit 63bb0d
		b, err := ioutil.ReadAll(pt.resp.Body)
Packit 63bb0d
		if err != nil {
Packit 63bb0d
			return autorest.NewErrorWithError(err, "pollingTrackerBase", "updateRawBody", nil, "failed to read response body")
Packit 63bb0d
		}
Packit 63bb0d
		// observed in 204 responses over HTTP/2.0; the content length is -1 but body is empty
Packit 63bb0d
		if len(b) == 0 {
Packit 63bb0d
			return nil
Packit 63bb0d
		}
Packit 63bb0d
		// put the body back so it's available to other callers
Packit 63bb0d
		pt.resp.Body = ioutil.NopCloser(bytes.NewReader(b))
Packit 63bb0d
		if err = json.Unmarshal(b, &pt.rawBody); err != nil {
Packit 63bb0d
			return autorest.NewErrorWithError(err, "pollingTrackerBase", "updateRawBody", nil, "failed to unmarshal response body")
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt *pollingTrackerBase) pollForStatus(ctx context.Context, sender autorest.Sender) error {
Packit 63bb0d
	req, err := http.NewRequest(http.MethodGet, pt.URI, nil)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return autorest.NewErrorWithError(err, "pollingTrackerBase", "pollForStatus", nil, "failed to create HTTP request")
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	req = req.WithContext(ctx)
Packit 63bb0d
	preparer := autorest.CreatePreparer(autorest.GetPrepareDecorators(ctx)...)
Packit 63bb0d
	req, err = preparer.Prepare(req)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return autorest.NewErrorWithError(err, "pollingTrackerBase", "pollForStatus", nil, "failed preparing HTTP request")
Packit 63bb0d
	}
Packit 63bb0d
	pt.resp, err = sender.Do(req)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return autorest.NewErrorWithError(err, "pollingTrackerBase", "pollForStatus", nil, "failed to send HTTP request")
Packit 63bb0d
	}
Packit 63bb0d
	if autorest.ResponseHasStatusCode(pt.resp, pollingCodes[:]...) {
Packit 63bb0d
		// reset the service error on success case
Packit 63bb0d
		pt.Err = nil
Packit 63bb0d
		err = pt.updateRawBody()
Packit 63bb0d
	} else {
Packit 63bb0d
		// check response body for error content
Packit 63bb0d
		pt.updateErrorFromResponse()
Packit 63bb0d
		err = pt.pollingError()
Packit 63bb0d
	}
Packit 63bb0d
	return err
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// attempts to unmarshal a ServiceError type from the response body.
Packit 63bb0d
// if that fails then make a best attempt at creating something meaningful.
Packit 63bb0d
// NOTE: this assumes that the async operation has failed.
Packit 63bb0d
func (pt *pollingTrackerBase) updateErrorFromResponse() {
Packit 63bb0d
	var err error
Packit 63bb0d
	if pt.resp.ContentLength != 0 {
Packit 63bb0d
		type respErr struct {
Packit 63bb0d
			ServiceError *ServiceError `json:"error"`
Packit 63bb0d
		}
Packit 63bb0d
		re := respErr{}
Packit 63bb0d
		defer pt.resp.Body.Close()
Packit 63bb0d
		var b []byte
Packit 63bb0d
		if b, err = ioutil.ReadAll(pt.resp.Body); err != nil || len(b) == 0 {
Packit 63bb0d
			goto Default
Packit 63bb0d
		}
Packit 63bb0d
		if err = json.Unmarshal(b, &re); err != nil {
Packit 63bb0d
			goto Default
Packit 63bb0d
		}
Packit 63bb0d
		// unmarshalling the error didn't yield anything, try unwrapped error
Packit 63bb0d
		if re.ServiceError == nil {
Packit 63bb0d
			err = json.Unmarshal(b, &re.ServiceError)
Packit 63bb0d
			if err != nil {
Packit 63bb0d
				goto Default
Packit 63bb0d
			}
Packit 63bb0d
		}
Packit 63bb0d
		// the unmarshaller will ensure re.ServiceError is non-nil
Packit 63bb0d
		// even if there was no content unmarshalled so check the code.
Packit 63bb0d
		if re.ServiceError.Code != "" {
Packit 63bb0d
			pt.Err = re.ServiceError
Packit 63bb0d
			return
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
Default:
Packit 63bb0d
	se := &ServiceError{
Packit 63bb0d
		Code:    pt.pollingStatus(),
Packit 63bb0d
		Message: "The async operation failed.",
Packit 63bb0d
	}
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		se.InnerError = make(map[string]interface{})
Packit 63bb0d
		se.InnerError["unmarshalError"] = err.Error()
Packit 63bb0d
	}
Packit 63bb0d
	// stick the response body into the error object in hopes
Packit 63bb0d
	// it contains something useful to help diagnose the failure.
Packit 63bb0d
	if len(pt.rawBody) > 0 {
Packit 63bb0d
		se.AdditionalInfo = []map[string]interface{}{
Packit 63bb0d
			pt.rawBody,
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
	pt.Err = se
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt *pollingTrackerBase) updatePollingState(provStateApl bool) error {
Packit 63bb0d
	if pt.Pm == PollingAsyncOperation && pt.rawBody["status"] != nil {
Packit 63bb0d
		pt.State = pt.rawBody["status"].(string)
Packit 63bb0d
	} else {
Packit 63bb0d
		if pt.resp.StatusCode == http.StatusAccepted {
Packit 63bb0d
			pt.State = operationInProgress
Packit 63bb0d
		} else if provStateApl {
Packit 63bb0d
			if ps := pt.getProvisioningState(); ps != nil {
Packit 63bb0d
				pt.State = *ps
Packit 63bb0d
			} else {
Packit 63bb0d
				pt.State = operationSucceeded
Packit 63bb0d
			}
Packit 63bb0d
		} else {
Packit 63bb0d
			return autorest.NewError("pollingTrackerBase", "updatePollingState", "the response from the async operation has an invalid status code")
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
	// if the operation has failed update the error state
Packit 63bb0d
	if pt.hasFailed() {
Packit 63bb0d
		pt.updateErrorFromResponse()
Packit 63bb0d
	}
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt pollingTrackerBase) pollingError() error {
Packit 63bb0d
	if pt.Err == nil {
Packit 63bb0d
		return nil
Packit 63bb0d
	}
Packit 63bb0d
	return pt.Err
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt pollingTrackerBase) pollingMethod() PollingMethodType {
Packit 63bb0d
	return pt.Pm
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt pollingTrackerBase) pollingStatus() string {
Packit 63bb0d
	return pt.State
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt pollingTrackerBase) pollingURL() string {
Packit 63bb0d
	return pt.URI
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt pollingTrackerBase) finalGetURL() string {
Packit 63bb0d
	return pt.FinalGetURI
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt pollingTrackerBase) hasTerminated() bool {
Packit 63bb0d
	return strings.EqualFold(pt.State, operationCanceled) || strings.EqualFold(pt.State, operationFailed) || strings.EqualFold(pt.State, operationSucceeded)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt pollingTrackerBase) hasFailed() bool {
Packit 63bb0d
	return strings.EqualFold(pt.State, operationCanceled) || strings.EqualFold(pt.State, operationFailed)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt pollingTrackerBase) hasSucceeded() bool {
Packit 63bb0d
	return strings.EqualFold(pt.State, operationSucceeded)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt pollingTrackerBase) latestResponse() *http.Response {
Packit 63bb0d
	return pt.resp
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// error checking common to all trackers
Packit 63bb0d
func (pt pollingTrackerBase) baseCheckForErrors() error {
Packit 63bb0d
	// for Azure-AsyncOperations the response body cannot be nil or empty
Packit 63bb0d
	if pt.Pm == PollingAsyncOperation {
Packit 63bb0d
		if pt.resp.Body == nil || pt.resp.ContentLength == 0 {
Packit 63bb0d
			return autorest.NewError("pollingTrackerBase", "baseCheckForErrors", "for Azure-AsyncOperation response body cannot be nil")
Packit 63bb0d
		}
Packit 63bb0d
		if pt.rawBody["status"] == nil {
Packit 63bb0d
			return autorest.NewError("pollingTrackerBase", "baseCheckForErrors", "missing status property in Azure-AsyncOperation response body")
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// default initialization of polling URL/method.  each verb tracker will update this as required.
Packit 63bb0d
func (pt *pollingTrackerBase) initPollingMethod() error {
Packit 63bb0d
	if ao, err := getURLFromAsyncOpHeader(pt.resp); err != nil {
Packit 63bb0d
		return err
Packit 63bb0d
	} else if ao != "" {
Packit 63bb0d
		pt.URI = ao
Packit 63bb0d
		pt.Pm = PollingAsyncOperation
Packit 63bb0d
		return nil
Packit 63bb0d
	}
Packit 63bb0d
	if lh, err := getURLFromLocationHeader(pt.resp); err != nil {
Packit 63bb0d
		return err
Packit 63bb0d
	} else if lh != "" {
Packit 63bb0d
		pt.URI = lh
Packit 63bb0d
		pt.Pm = PollingLocation
Packit 63bb0d
		return nil
Packit 63bb0d
	}
Packit 63bb0d
	// it's ok if we didn't find a polling header, this will be handled elsewhere
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// DELETE
Packit 63bb0d
Packit 63bb0d
type pollingTrackerDelete struct {
Packit 63bb0d
	pollingTrackerBase
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt *pollingTrackerDelete) updatePollingMethod() error {
Packit 63bb0d
	// for 201 the Location header is required
Packit 63bb0d
	if pt.resp.StatusCode == http.StatusCreated {
Packit 63bb0d
		if lh, err := getURLFromLocationHeader(pt.resp); err != nil {
Packit 63bb0d
			return err
Packit 63bb0d
		} else if lh == "" {
Packit 63bb0d
			return autorest.NewError("pollingTrackerDelete", "updateHeaders", "missing Location header in 201 response")
Packit 63bb0d
		} else {
Packit 63bb0d
			pt.URI = lh
Packit 63bb0d
		}
Packit 63bb0d
		pt.Pm = PollingLocation
Packit 63bb0d
		pt.FinalGetURI = pt.URI
Packit 63bb0d
	}
Packit 63bb0d
	// for 202 prefer the Azure-AsyncOperation header but fall back to Location if necessary
Packit 63bb0d
	if pt.resp.StatusCode == http.StatusAccepted {
Packit 63bb0d
		ao, err := getURLFromAsyncOpHeader(pt.resp)
Packit 63bb0d
		if err != nil {
Packit 63bb0d
			return err
Packit 63bb0d
		} else if ao != "" {
Packit 63bb0d
			pt.URI = ao
Packit 63bb0d
			pt.Pm = PollingAsyncOperation
Packit 63bb0d
		}
Packit 63bb0d
		// if the Location header is invalid and we already have a polling URL
Packit 63bb0d
		// then we don't care if the Location header URL is malformed.
Packit 63bb0d
		if lh, err := getURLFromLocationHeader(pt.resp); err != nil && pt.URI == "" {
Packit 63bb0d
			return err
Packit 63bb0d
		} else if lh != "" {
Packit 63bb0d
			if ao == "" {
Packit 63bb0d
				pt.URI = lh
Packit 63bb0d
				pt.Pm = PollingLocation
Packit 63bb0d
			}
Packit 63bb0d
			// when both headers are returned we use the value in the Location header for the final GET
Packit 63bb0d
			pt.FinalGetURI = lh
Packit 63bb0d
		}
Packit 63bb0d
		// make sure a polling URL was found
Packit 63bb0d
		if pt.URI == "" {
Packit 63bb0d
			return autorest.NewError("pollingTrackerPost", "updateHeaders", "didn't get any suitable polling URLs in 202 response")
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt pollingTrackerDelete) checkForErrors() error {
Packit 63bb0d
	return pt.baseCheckForErrors()
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt pollingTrackerDelete) provisioningStateApplicable() bool {
Packit 63bb0d
	return pt.resp.StatusCode == http.StatusOK || pt.resp.StatusCode == http.StatusNoContent
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// PATCH
Packit 63bb0d
Packit 63bb0d
type pollingTrackerPatch struct {
Packit 63bb0d
	pollingTrackerBase
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt *pollingTrackerPatch) updatePollingMethod() error {
Packit 63bb0d
	// by default we can use the original URL for polling and final GET
Packit 63bb0d
	if pt.URI == "" {
Packit 63bb0d
		pt.URI = pt.resp.Request.URL.String()
Packit 63bb0d
	}
Packit 63bb0d
	if pt.FinalGetURI == "" {
Packit 63bb0d
		pt.FinalGetURI = pt.resp.Request.URL.String()
Packit 63bb0d
	}
Packit 63bb0d
	if pt.Pm == PollingUnknown {
Packit 63bb0d
		pt.Pm = PollingRequestURI
Packit 63bb0d
	}
Packit 63bb0d
	// for 201 it's permissible for no headers to be returned
Packit 63bb0d
	if pt.resp.StatusCode == http.StatusCreated {
Packit 63bb0d
		if ao, err := getURLFromAsyncOpHeader(pt.resp); err != nil {
Packit 63bb0d
			return err
Packit 63bb0d
		} else if ao != "" {
Packit 63bb0d
			pt.URI = ao
Packit 63bb0d
			pt.Pm = PollingAsyncOperation
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
	// for 202 prefer the Azure-AsyncOperation header but fall back to Location if necessary
Packit 63bb0d
	// note the absence of the "final GET" mechanism for PATCH
Packit 63bb0d
	if pt.resp.StatusCode == http.StatusAccepted {
Packit 63bb0d
		ao, err := getURLFromAsyncOpHeader(pt.resp)
Packit 63bb0d
		if err != nil {
Packit 63bb0d
			return err
Packit 63bb0d
		} else if ao != "" {
Packit 63bb0d
			pt.URI = ao
Packit 63bb0d
			pt.Pm = PollingAsyncOperation
Packit 63bb0d
		}
Packit 63bb0d
		if ao == "" {
Packit 63bb0d
			if lh, err := getURLFromLocationHeader(pt.resp); err != nil {
Packit 63bb0d
				return err
Packit 63bb0d
			} else if lh == "" {
Packit 63bb0d
				return autorest.NewError("pollingTrackerPatch", "updateHeaders", "didn't get any suitable polling URLs in 202 response")
Packit 63bb0d
			} else {
Packit 63bb0d
				pt.URI = lh
Packit 63bb0d
				pt.Pm = PollingLocation
Packit 63bb0d
			}
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt pollingTrackerPatch) checkForErrors() error {
Packit 63bb0d
	return pt.baseCheckForErrors()
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt pollingTrackerPatch) provisioningStateApplicable() bool {
Packit 63bb0d
	return pt.resp.StatusCode == http.StatusOK || pt.resp.StatusCode == http.StatusCreated
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// POST
Packit 63bb0d
Packit 63bb0d
type pollingTrackerPost struct {
Packit 63bb0d
	pollingTrackerBase
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt *pollingTrackerPost) updatePollingMethod() error {
Packit 63bb0d
	// 201 requires Location header
Packit 63bb0d
	if pt.resp.StatusCode == http.StatusCreated {
Packit 63bb0d
		if lh, err := getURLFromLocationHeader(pt.resp); err != nil {
Packit 63bb0d
			return err
Packit 63bb0d
		} else if lh == "" {
Packit 63bb0d
			return autorest.NewError("pollingTrackerPost", "updateHeaders", "missing Location header in 201 response")
Packit 63bb0d
		} else {
Packit 63bb0d
			pt.URI = lh
Packit 63bb0d
			pt.FinalGetURI = lh
Packit 63bb0d
			pt.Pm = PollingLocation
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
	// for 202 prefer the Azure-AsyncOperation header but fall back to Location if necessary
Packit 63bb0d
	if pt.resp.StatusCode == http.StatusAccepted {
Packit 63bb0d
		ao, err := getURLFromAsyncOpHeader(pt.resp)
Packit 63bb0d
		if err != nil {
Packit 63bb0d
			return err
Packit 63bb0d
		} else if ao != "" {
Packit 63bb0d
			pt.URI = ao
Packit 63bb0d
			pt.Pm = PollingAsyncOperation
Packit 63bb0d
		}
Packit 63bb0d
		// if the Location header is invalid and we already have a polling URL
Packit 63bb0d
		// then we don't care if the Location header URL is malformed.
Packit 63bb0d
		if lh, err := getURLFromLocationHeader(pt.resp); err != nil && pt.URI == "" {
Packit 63bb0d
			return err
Packit 63bb0d
		} else if lh != "" {
Packit 63bb0d
			if ao == "" {
Packit 63bb0d
				pt.URI = lh
Packit 63bb0d
				pt.Pm = PollingLocation
Packit 63bb0d
			}
Packit 63bb0d
			// when both headers are returned we use the value in the Location header for the final GET
Packit 63bb0d
			pt.FinalGetURI = lh
Packit 63bb0d
		}
Packit 63bb0d
		// make sure a polling URL was found
Packit 63bb0d
		if pt.URI == "" {
Packit 63bb0d
			return autorest.NewError("pollingTrackerPost", "updateHeaders", "didn't get any suitable polling URLs in 202 response")
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt pollingTrackerPost) checkForErrors() error {
Packit 63bb0d
	return pt.baseCheckForErrors()
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt pollingTrackerPost) provisioningStateApplicable() bool {
Packit 63bb0d
	return pt.resp.StatusCode == http.StatusOK || pt.resp.StatusCode == http.StatusNoContent
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// PUT
Packit 63bb0d
Packit 63bb0d
type pollingTrackerPut struct {
Packit 63bb0d
	pollingTrackerBase
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt *pollingTrackerPut) updatePollingMethod() error {
Packit 63bb0d
	// by default we can use the original URL for polling and final GET
Packit 63bb0d
	if pt.URI == "" {
Packit 63bb0d
		pt.URI = pt.resp.Request.URL.String()
Packit 63bb0d
	}
Packit 63bb0d
	if pt.FinalGetURI == "" {
Packit 63bb0d
		pt.FinalGetURI = pt.resp.Request.URL.String()
Packit 63bb0d
	}
Packit 63bb0d
	if pt.Pm == PollingUnknown {
Packit 63bb0d
		pt.Pm = PollingRequestURI
Packit 63bb0d
	}
Packit 63bb0d
	// for 201 it's permissible for no headers to be returned
Packit 63bb0d
	if pt.resp.StatusCode == http.StatusCreated {
Packit 63bb0d
		if ao, err := getURLFromAsyncOpHeader(pt.resp); err != nil {
Packit 63bb0d
			return err
Packit 63bb0d
		} else if ao != "" {
Packit 63bb0d
			pt.URI = ao
Packit 63bb0d
			pt.Pm = PollingAsyncOperation
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
	// for 202 prefer the Azure-AsyncOperation header but fall back to Location if necessary
Packit 63bb0d
	if pt.resp.StatusCode == http.StatusAccepted {
Packit 63bb0d
		ao, err := getURLFromAsyncOpHeader(pt.resp)
Packit 63bb0d
		if err != nil {
Packit 63bb0d
			return err
Packit 63bb0d
		} else if ao != "" {
Packit 63bb0d
			pt.URI = ao
Packit 63bb0d
			pt.Pm = PollingAsyncOperation
Packit 63bb0d
		}
Packit 63bb0d
		// if the Location header is invalid and we already have a polling URL
Packit 63bb0d
		// then we don't care if the Location header URL is malformed.
Packit 63bb0d
		if lh, err := getURLFromLocationHeader(pt.resp); err != nil && pt.URI == "" {
Packit 63bb0d
			return err
Packit 63bb0d
		} else if lh != "" {
Packit 63bb0d
			if ao == "" {
Packit 63bb0d
				pt.URI = lh
Packit 63bb0d
				pt.Pm = PollingLocation
Packit 63bb0d
			}
Packit 63bb0d
		}
Packit 63bb0d
		// make sure a polling URL was found
Packit 63bb0d
		if pt.URI == "" {
Packit 63bb0d
			return autorest.NewError("pollingTrackerPut", "updateHeaders", "didn't get any suitable polling URLs in 202 response")
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt pollingTrackerPut) checkForErrors() error {
Packit 63bb0d
	err := pt.baseCheckForErrors()
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return err
Packit 63bb0d
	}
Packit 63bb0d
	// if there are no LRO headers then the body cannot be empty
Packit 63bb0d
	ao, err := getURLFromAsyncOpHeader(pt.resp)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return err
Packit 63bb0d
	}
Packit 63bb0d
	lh, err := getURLFromLocationHeader(pt.resp)
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		return err
Packit 63bb0d
	}
Packit 63bb0d
	if ao == "" && lh == "" && len(pt.rawBody) == 0 {
Packit 63bb0d
		return autorest.NewError("pollingTrackerPut", "checkForErrors", "the response did not contain a body")
Packit 63bb0d
	}
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (pt pollingTrackerPut) provisioningStateApplicable() bool {
Packit 63bb0d
	return pt.resp.StatusCode == http.StatusOK || pt.resp.StatusCode == http.StatusCreated
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// creates a polling tracker based on the verb of the original request
Packit 63bb0d
func createPollingTracker(resp *http.Response) (pollingTracker, error) {
Packit 63bb0d
	var pt pollingTracker
Packit 63bb0d
	switch strings.ToUpper(resp.Request.Method) {
Packit 63bb0d
	case http.MethodDelete:
Packit 63bb0d
		pt = &pollingTrackerDelete{pollingTrackerBase: pollingTrackerBase{resp: resp}}
Packit 63bb0d
	case http.MethodPatch:
Packit 63bb0d
		pt = &pollingTrackerPatch{pollingTrackerBase: pollingTrackerBase{resp: resp}}
Packit 63bb0d
	case http.MethodPost:
Packit 63bb0d
		pt = &pollingTrackerPost{pollingTrackerBase: pollingTrackerBase{resp: resp}}
Packit 63bb0d
	case http.MethodPut:
Packit 63bb0d
		pt = &pollingTrackerPut{pollingTrackerBase: pollingTrackerBase{resp: resp}}
Packit 63bb0d
	default:
Packit 63bb0d
		return nil, autorest.NewError("azure", "createPollingTracker", "unsupported HTTP method %s", resp.Request.Method)
Packit 63bb0d
	}
Packit 63bb0d
	if err := pt.initializeState(); err != nil {
Packit 63bb0d
		return pt, err
Packit 63bb0d
	}
Packit 63bb0d
	// this initializes the polling header values, we do this during creation in case the
Packit 63bb0d
	// initial response send us invalid values; this way the API call will return a non-nil
Packit 63bb0d
	// error (not doing this means the error shows up in Future.Done)
Packit 63bb0d
	return pt, pt.updatePollingMethod()
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// gets the polling URL from the Azure-AsyncOperation header.
Packit 63bb0d
// ensures the URL is well-formed and absolute.
Packit 63bb0d
func getURLFromAsyncOpHeader(resp *http.Response) (string, error) {
Packit 63bb0d
	s := resp.Header.Get(http.CanonicalHeaderKey(headerAsyncOperation))
Packit 63bb0d
	if s == "" {
Packit 63bb0d
		return "", nil
Packit 63bb0d
	}
Packit 63bb0d
	if !isValidURL(s) {
Packit 63bb0d
		return "", autorest.NewError("azure", "getURLFromAsyncOpHeader", "invalid polling URL '%s'", s)
Packit 63bb0d
	}
Packit 63bb0d
	return s, nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// gets the polling URL from the Location header.
Packit 63bb0d
// ensures the URL is well-formed and absolute.
Packit 63bb0d
func getURLFromLocationHeader(resp *http.Response) (string, error) {
Packit 63bb0d
	s := resp.Header.Get(http.CanonicalHeaderKey(autorest.HeaderLocation))
Packit 63bb0d
	if s == "" {
Packit 63bb0d
		return "", nil
Packit 63bb0d
	}
Packit 63bb0d
	if !isValidURL(s) {
Packit 63bb0d
		return "", autorest.NewError("azure", "getURLFromLocationHeader", "invalid polling URL '%s'", s)
Packit 63bb0d
	}
Packit 63bb0d
	return s, nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// verify that the URL is valid and absolute
Packit 63bb0d
func isValidURL(s string) bool {
Packit 63bb0d
	u, err := url.Parse(s)
Packit 63bb0d
	return err == nil && u.IsAbs()
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// PollingMethodType defines a type used for enumerating polling mechanisms.
Packit 63bb0d
type PollingMethodType string
Packit 63bb0d
Packit 63bb0d
const (
Packit 63bb0d
	// PollingAsyncOperation indicates the polling method uses the Azure-AsyncOperation header.
Packit 63bb0d
	PollingAsyncOperation PollingMethodType = "AsyncOperation"
Packit 63bb0d
Packit 63bb0d
	// PollingLocation indicates the polling method uses the Location header.
Packit 63bb0d
	PollingLocation PollingMethodType = "Location"
Packit 63bb0d
Packit 63bb0d
	// PollingRequestURI indicates the polling method uses the original request URI.
Packit 63bb0d
	PollingRequestURI PollingMethodType = "RequestURI"
Packit 63bb0d
Packit 63bb0d
	// PollingUnknown indicates an unknown polling method and is the default value.
Packit 63bb0d
	PollingUnknown PollingMethodType = ""
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
// AsyncOpIncompleteError is the type that's returned from a future that has not completed.
Packit 63bb0d
type AsyncOpIncompleteError struct {
Packit 63bb0d
	// FutureType is the name of the type composed of a azure.Future.
Packit 63bb0d
	FutureType string
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Error returns an error message including the originating type name of the error.
Packit 63bb0d
func (e AsyncOpIncompleteError) Error() string {
Packit 63bb0d
	return fmt.Sprintf("%s: asynchronous operation has not completed", e.FutureType)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// NewAsyncOpIncompleteError creates a new AsyncOpIncompleteError with the specified parameters.
Packit 63bb0d
func NewAsyncOpIncompleteError(futureType string) AsyncOpIncompleteError {
Packit 63bb0d
	return AsyncOpIncompleteError{
Packit 63bb0d
		FutureType: futureType,
Packit 63bb0d
	}
Packit 63bb0d
}