...

Source file src/github.com/Azure/go-autorest/autorest/autorest.go

Documentation: github.com/Azure/go-autorest/autorest

     1  /*
     2  Package autorest implements an HTTP request pipeline suitable for use across multiple go-routines
     3  and provides the shared routines relied on by AutoRest (see https://github.com/Azure/autorest/)
     4  generated Go code.
     5  
     6  The package breaks sending and responding to HTTP requests into three phases: Preparing, Sending,
     7  and Responding. A typical pattern is:
     8  
     9  	req, err := Prepare(&http.Request{},
    10  	  token.WithAuthorization())
    11  
    12  	resp, err := Send(req,
    13  	  WithLogging(logger),
    14  	  DoErrorIfStatusCode(http.StatusInternalServerError),
    15  	  DoCloseIfError(),
    16  	  DoRetryForAttempts(5, time.Second))
    17  
    18  	err = Respond(resp,
    19  	  ByDiscardingBody(),
    20  	  ByClosing())
    21  
    22  Each phase relies on decorators to modify and / or manage processing. Decorators may first modify
    23  and then pass the data along, pass the data first and then modify the result, or wrap themselves
    24  around passing the data (such as a logger might do). Decorators run in the order provided. For
    25  example, the following:
    26  
    27  	req, err := Prepare(&http.Request{},
    28  	  WithBaseURL("https://microsoft.com/"),
    29  	  WithPath("a"),
    30  	  WithPath("b"),
    31  	  WithPath("c"))
    32  
    33  will set the URL to:
    34  
    35  	https://microsoft.com/a/b/c
    36  
    37  Preparers and Responders may be shared and re-used (assuming the underlying decorators support
    38  sharing and re-use). Performant use is obtained by creating one or more Preparers and Responders
    39  shared among multiple go-routines, and a single Sender shared among multiple sending go-routines,
    40  all bound together by means of input / output channels.
    41  
    42  Decorators hold their passed state within a closure (such as the path components in the example
    43  above). Be careful to share Preparers and Responders only in a context where such held state
    44  applies. For example, it may not make sense to share a Preparer that applies a query string from a
    45  fixed set of values. Similarly, sharing a Responder that reads the response body into a passed
    46  struct (e.g., ByUnmarshallingJson) is likely incorrect.
    47  
    48  Lastly, the Swagger specification (https://swagger.io) that drives AutoRest
    49  (https://github.com/Azure/autorest/) precisely defines two date forms: date and date-time. The
    50  github.com/Azure/go-autorest/autorest/date package provides time.Time derivations to ensure
    51  correct parsing and formatting.
    52  
    53  Errors raised by autorest objects and methods will conform to the autorest.Error interface.
    54  
    55  See the included examples for more detail. For details on the suggested use of this package by
    56  generated clients, see the Client described below.
    57  */
    58  package autorest
    59  
    60  // Copyright 2017 Microsoft Corporation
    61  //
    62  //  Licensed under the Apache License, Version 2.0 (the "License");
    63  //  you may not use this file except in compliance with the License.
    64  //  You may obtain a copy of the License at
    65  //
    66  //      http://www.apache.org/licenses/LICENSE-2.0
    67  //
    68  //  Unless required by applicable law or agreed to in writing, software
    69  //  distributed under the License is distributed on an "AS IS" BASIS,
    70  //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    71  //  See the License for the specific language governing permissions and
    72  //  limitations under the License.
    73  
    74  import (
    75  	"context"
    76  	"net/http"
    77  	"time"
    78  )
    79  
    80  const (
    81  	// HeaderLocation specifies the HTTP Location header.
    82  	HeaderLocation = "Location"
    83  
    84  	// HeaderRetryAfter specifies the HTTP Retry-After header.
    85  	HeaderRetryAfter = "Retry-After"
    86  )
    87  
    88  // ResponseHasStatusCode returns true if the status code in the HTTP Response is in the passed set
    89  // and false otherwise.
    90  func ResponseHasStatusCode(resp *http.Response, codes ...int) bool {
    91  	if resp == nil {
    92  		return false
    93  	}
    94  	return containsInt(codes, resp.StatusCode)
    95  }
    96  
    97  // GetLocation retrieves the URL from the Location header of the passed response.
    98  func GetLocation(resp *http.Response) string {
    99  	return resp.Header.Get(HeaderLocation)
   100  }
   101  
   102  // GetRetryAfter extracts the retry delay from the Retry-After header of the passed response. If
   103  // the header is absent or is malformed, it will return the supplied default delay time.Duration.
   104  func GetRetryAfter(resp *http.Response, defaultDelay time.Duration) time.Duration {
   105  	retry := resp.Header.Get(HeaderRetryAfter)
   106  	if retry == "" {
   107  		return defaultDelay
   108  	}
   109  
   110  	d, err := time.ParseDuration(retry + "s")
   111  	if err != nil {
   112  		return defaultDelay
   113  	}
   114  
   115  	return d
   116  }
   117  
   118  // NewPollingRequest allocates and returns a new http.Request to poll for the passed response.
   119  func NewPollingRequest(resp *http.Response, cancel <-chan struct{}) (*http.Request, error) {
   120  	location := GetLocation(resp)
   121  	if location == "" {
   122  		return nil, NewErrorWithResponse("autorest", "NewPollingRequest", resp, "Location header missing from response that requires polling")
   123  	}
   124  
   125  	req, err := Prepare(&http.Request{Cancel: cancel},
   126  		AsGet(),
   127  		WithBaseURL(location))
   128  	if err != nil {
   129  		return nil, NewErrorWithError(err, "autorest", "NewPollingRequest", nil, "Failure creating poll request to %s", location)
   130  	}
   131  
   132  	return req, nil
   133  }
   134  
   135  // NewPollingRequestWithContext allocates and returns a new http.Request with the specified context to poll for the passed response.
   136  func NewPollingRequestWithContext(ctx context.Context, resp *http.Response) (*http.Request, error) {
   137  	location := GetLocation(resp)
   138  	if location == "" {
   139  		return nil, NewErrorWithResponse("autorest", "NewPollingRequestWithContext", resp, "Location header missing from response that requires polling")
   140  	}
   141  
   142  	req, err := Prepare((&http.Request{}).WithContext(ctx),
   143  		AsGet(),
   144  		WithBaseURL(location))
   145  	if err != nil {
   146  		return nil, NewErrorWithError(err, "autorest", "NewPollingRequestWithContext", nil, "Failure creating poll request to %s", location)
   147  	}
   148  
   149  	return req, nil
   150  }
   151  

View as plain text