|
Packit |
63bb0d |
/*
|
|
Packit |
63bb0d |
Package autorest implements an HTTP request pipeline suitable for use across multiple go-routines
|
|
Packit |
63bb0d |
and provides the shared routines relied on by AutoRest (see https://github.com/Azure/autorest/)
|
|
Packit |
63bb0d |
generated Go code.
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
The package breaks sending and responding to HTTP requests into three phases: Preparing, Sending,
|
|
Packit |
63bb0d |
and Responding. A typical pattern is:
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
req, err := Prepare(&http.Request{},
|
|
Packit |
63bb0d |
token.WithAuthorization())
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
resp, err := Send(req,
|
|
Packit |
63bb0d |
WithLogging(logger),
|
|
Packit |
63bb0d |
DoErrorIfStatusCode(http.StatusInternalServerError),
|
|
Packit |
63bb0d |
DoCloseIfError(),
|
|
Packit |
63bb0d |
DoRetryForAttempts(5, time.Second))
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
err = Respond(resp,
|
|
Packit |
63bb0d |
ByDiscardingBody(),
|
|
Packit |
63bb0d |
ByClosing())
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
Each phase relies on decorators to modify and / or manage processing. Decorators may first modify
|
|
Packit |
63bb0d |
and then pass the data along, pass the data first and then modify the result, or wrap themselves
|
|
Packit |
63bb0d |
around passing the data (such as a logger might do). Decorators run in the order provided. For
|
|
Packit |
63bb0d |
example, the following:
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
req, err := Prepare(&http.Request{},
|
|
Packit |
63bb0d |
WithBaseURL("https://microsoft.com/"),
|
|
Packit |
63bb0d |
WithPath("a"),
|
|
Packit |
63bb0d |
WithPath("b"),
|
|
Packit |
63bb0d |
WithPath("c"))
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
will set the URL to:
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
https://microsoft.com/a/b/c
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
Preparers and Responders may be shared and re-used (assuming the underlying decorators support
|
|
Packit |
63bb0d |
sharing and re-use). Performant use is obtained by creating one or more Preparers and Responders
|
|
Packit |
63bb0d |
shared among multiple go-routines, and a single Sender shared among multiple sending go-routines,
|
|
Packit |
63bb0d |
all bound together by means of input / output channels.
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
Decorators hold their passed state within a closure (such as the path components in the example
|
|
Packit |
63bb0d |
above). Be careful to share Preparers and Responders only in a context where such held state
|
|
Packit |
63bb0d |
applies. For example, it may not make sense to share a Preparer that applies a query string from a
|
|
Packit |
63bb0d |
fixed set of values. Similarly, sharing a Responder that reads the response body into a passed
|
|
Packit |
63bb0d |
struct (e.g., ByUnmarshallingJson) is likely incorrect.
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
Lastly, the Swagger specification (https://swagger.io) that drives AutoRest
|
|
Packit |
63bb0d |
(https://github.com/Azure/autorest/) precisely defines two date forms: date and date-time. The
|
|
Packit |
63bb0d |
github.com/Azure/go-autorest/autorest/date package provides time.Time derivations to ensure
|
|
Packit |
63bb0d |
correct parsing and formatting.
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
Errors raised by autorest objects and methods will conform to the autorest.Error interface.
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
See the included examples for more detail. For details on the suggested use of this package by
|
|
Packit |
63bb0d |
generated clients, see the Client described below.
|
|
Packit |
63bb0d |
*/
|
|
Packit |
63bb0d |
package autorest
|
|
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 |
"context"
|
|
Packit |
63bb0d |
"net/http"
|
|
Packit |
63bb0d |
"time"
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
const (
|
|
Packit |
63bb0d |
// HeaderLocation specifies the HTTP Location header.
|
|
Packit |
63bb0d |
HeaderLocation = "Location"
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// HeaderRetryAfter specifies the HTTP Retry-After header.
|
|
Packit |
63bb0d |
HeaderRetryAfter = "Retry-After"
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// ResponseHasStatusCode returns true if the status code in the HTTP Response is in the passed set
|
|
Packit |
63bb0d |
// and false otherwise.
|
|
Packit |
63bb0d |
func ResponseHasStatusCode(resp *http.Response, codes ...int) bool {
|
|
Packit |
63bb0d |
if resp == nil {
|
|
Packit |
63bb0d |
return false
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
return containsInt(codes, resp.StatusCode)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// GetLocation retrieves the URL from the Location header of the passed response.
|
|
Packit |
63bb0d |
func GetLocation(resp *http.Response) string {
|
|
Packit |
63bb0d |
return resp.Header.Get(HeaderLocation)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// GetRetryAfter extracts the retry delay from the Retry-After header of the passed response. If
|
|
Packit |
63bb0d |
// the header is absent or is malformed, it will return the supplied default delay time.Duration.
|
|
Packit |
63bb0d |
func GetRetryAfter(resp *http.Response, defaultDelay time.Duration) time.Duration {
|
|
Packit |
63bb0d |
retry := resp.Header.Get(HeaderRetryAfter)
|
|
Packit |
63bb0d |
if retry == "" {
|
|
Packit |
63bb0d |
return defaultDelay
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
d, err := time.ParseDuration(retry + "s")
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return defaultDelay
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return d
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// NewPollingRequest allocates and returns a new http.Request to poll for the passed response.
|
|
Packit |
63bb0d |
func NewPollingRequest(resp *http.Response, cancel <-chan struct{}) (*http.Request, error) {
|
|
Packit |
63bb0d |
location := GetLocation(resp)
|
|
Packit |
63bb0d |
if location == "" {
|
|
Packit |
63bb0d |
return nil, NewErrorWithResponse("autorest", "NewPollingRequest", resp, "Location header missing from response that requires polling")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
req, err := Prepare(&http.Request{Cancel: cancel},
|
|
Packit |
63bb0d |
AsGet(),
|
|
Packit |
63bb0d |
WithBaseURL(location))
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return nil, NewErrorWithError(err, "autorest", "NewPollingRequest", nil, "Failure creating poll request to %s", location)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return req, nil
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// NewPollingRequestWithContext allocates and returns a new http.Request with the specified context to poll for the passed response.
|
|
Packit |
63bb0d |
func NewPollingRequestWithContext(ctx context.Context, resp *http.Response) (*http.Request, error) {
|
|
Packit |
63bb0d |
location := GetLocation(resp)
|
|
Packit |
63bb0d |
if location == "" {
|
|
Packit |
63bb0d |
return nil, NewErrorWithResponse("autorest", "NewPollingRequestWithContext", resp, "Location header missing from response that requires polling")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
req, err := Prepare((&http.Request{}).WithContext(ctx),
|
|
Packit |
63bb0d |
AsGet(),
|
|
Packit |
63bb0d |
WithBaseURL(location))
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return nil, NewErrorWithError(err, "autorest", "NewPollingRequestWithContext", nil, "Failure creating poll request to %s", location)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return req, nil
|
|
Packit |
63bb0d |
}
|