...

Source file src/github.com/Azure/azure-sdk-for-go/services/classic/management/http.go

Documentation: github.com/Azure/azure-sdk-for-go/services/classic/management

     1  // +build go1.7
     2  
     3  package management
     4  
     5  // Copyright (c) Microsoft Corporation. All rights reserved.
     6  // Licensed under the MIT License. See License.txt in the project root for license information.
     7  
     8  import (
     9  	"bytes"
    10  	"crypto/tls"
    11  	"fmt"
    12  	"net/http"
    13  	"time"
    14  )
    15  
    16  const (
    17  	msVersionHeader           = "x-ms-version"
    18  	requestIDHeader           = "x-ms-request-id"
    19  	uaHeader                  = "User-Agent"
    20  	contentHeader             = "Content-Type"
    21  	defaultContentHeaderValue = "application/xml"
    22  )
    23  
    24  func (client client) SendAzureGetRequest(url string) ([]byte, error) {
    25  	resp, err := client.sendAzureRequest("GET", url, "", nil)
    26  	if err != nil {
    27  		return nil, err
    28  	}
    29  	return getResponseBody(resp)
    30  }
    31  
    32  func (client client) SendAzurePostRequest(url string, data []byte) (OperationID, error) {
    33  	return client.doAzureOperation("POST", url, "", data)
    34  }
    35  
    36  func (client client) SendAzurePostRequestWithReturnedResponse(url string, data []byte) ([]byte, error) {
    37  	resp, err := client.sendAzureRequest("POST", url, "", data)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	return getResponseBody(resp)
    43  }
    44  
    45  func (client client) SendAzurePutRequest(url, contentType string, data []byte) (OperationID, error) {
    46  	return client.doAzureOperation("PUT", url, contentType, data)
    47  }
    48  
    49  func (client client) SendAzureDeleteRequest(url string) (OperationID, error) {
    50  	return client.doAzureOperation("DELETE", url, "", nil)
    51  }
    52  
    53  func (client client) doAzureOperation(method, url, contentType string, data []byte) (OperationID, error) {
    54  	response, err := client.sendAzureRequest(method, url, contentType, data)
    55  	if err != nil {
    56  		return "", err
    57  	}
    58  	return getOperationID(response)
    59  }
    60  
    61  func getOperationID(response *http.Response) (OperationID, error) {
    62  	requestID := response.Header.Get(requestIDHeader)
    63  	if requestID == "" {
    64  		return "", fmt.Errorf("Could not retrieve operation id from %q header", requestIDHeader)
    65  	}
    66  	return OperationID(requestID), nil
    67  }
    68  
    69  // sendAzureRequest constructs an HTTP client for the request, sends it to the
    70  // management API and returns the response or an error.
    71  func (client client) sendAzureRequest(method, url, contentType string, data []byte) (*http.Response, error) {
    72  	if method == "" {
    73  		return nil, fmt.Errorf(errParamNotSpecified, "method")
    74  	}
    75  	if url == "" {
    76  		return nil, fmt.Errorf(errParamNotSpecified, "url")
    77  	}
    78  
    79  	response, err := client.sendRequest(client.httpClient, url, method, contentType, data, 5)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	return response, nil
    85  }
    86  
    87  // createHTTPClient creates an HTTP Client configured with the key pair for
    88  // the subscription for this client.
    89  func (client client) createHTTPClient() (*http.Client, error) {
    90  	cert, err := tls.X509KeyPair(client.publishSettings.SubscriptionCert, client.publishSettings.SubscriptionKey)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	return &http.Client{
    96  		Transport: &http.Transport{
    97  			Proxy: http.ProxyFromEnvironment,
    98  			TLSClientConfig: &tls.Config{
    99  				Renegotiation: tls.RenegotiateOnceAsClient,
   100  				Certificates:  []tls.Certificate{cert},
   101  			},
   102  		},
   103  	}, nil
   104  }
   105  
   106  // sendRequest sends a request to the Azure management API using the given
   107  // HTTP client and parameters. It returns the response from the call or an
   108  // error.
   109  func (client client) sendRequest(httpClient *http.Client, url, requestType, contentType string, data []byte, numberOfRetries int) (*http.Response, error) {
   110  
   111  	absURI := client.createAzureRequestURI(url)
   112  
   113  	for {
   114  		request, reqErr := client.createAzureRequest(absURI, requestType, contentType, data)
   115  		if reqErr != nil {
   116  			return nil, reqErr
   117  		}
   118  
   119  		response, err := httpClient.Do(request)
   120  		if err != nil {
   121  			if numberOfRetries == 0 {
   122  				return nil, err
   123  			}
   124  
   125  			return client.sendRequest(httpClient, url, requestType, contentType, data, numberOfRetries-1)
   126  		}
   127  		if response.StatusCode == http.StatusTemporaryRedirect {
   128  			// ASM's way of moving traffic around, see https://msdn.microsoft.com/en-us/library/azure/ee460801.aspx
   129  			// Only handled automatically for GET/HEAD requests. This is for the rest of the http verbs.
   130  			u, err := response.Location()
   131  			if err != nil {
   132  				return response, fmt.Errorf("Redirect requested but location header could not be retrieved: %v", err)
   133  			}
   134  			absURI = u.String()
   135  			continue // re-issue request
   136  		}
   137  
   138  		if response.StatusCode >= http.StatusBadRequest {
   139  			body, err := getResponseBody(response)
   140  			if err != nil {
   141  				// Failed to read the response body
   142  				return nil, err
   143  			}
   144  			azureErr := getAzureError(body)
   145  			if azureErr != nil {
   146  				if numberOfRetries == 0 {
   147  					return nil, azureErr
   148  				}
   149  				if response.StatusCode == http.StatusServiceUnavailable || response.StatusCode == http.StatusTooManyRequests {
   150  					// Wait before retrying the operation
   151  					time.Sleep(client.config.OperationPollInterval)
   152  				}
   153  
   154  				return client.sendRequest(httpClient, url, requestType, contentType, data, numberOfRetries-1)
   155  			}
   156  		}
   157  
   158  		return response, nil
   159  	}
   160  }
   161  
   162  // createAzureRequestURI constructs the request uri using the management API endpoint and
   163  // subscription ID associated with the client.
   164  func (client client) createAzureRequestURI(url string) string {
   165  	return fmt.Sprintf("%s/%s/%s", client.config.ManagementURL, client.publishSettings.SubscriptionID, url)
   166  }
   167  
   168  // createAzureRequest packages up the request with the correct set of headers and returns
   169  // the request object or an error.
   170  func (client client) createAzureRequest(url string, requestType string, contentType string, data []byte) (*http.Request, error) {
   171  	var request *http.Request
   172  	var err error
   173  
   174  	if data != nil {
   175  		body := bytes.NewBuffer(data)
   176  		request, err = http.NewRequest(requestType, url, body)
   177  	} else {
   178  		request, err = http.NewRequest(requestType, url, nil)
   179  	}
   180  
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  
   185  	request.Header.Set(msVersionHeader, client.config.APIVersion)
   186  	request.Header.Set(uaHeader, client.config.UserAgent)
   187  
   188  	if contentType != "" {
   189  		request.Header.Set(contentHeader, contentType)
   190  	} else {
   191  		request.Header.Set(contentHeader, defaultContentHeaderValue)
   192  	}
   193  
   194  	return request, nil
   195  }
   196  

View as plain text