pax_global_header00006660000000000000000000000064125631455130014517gustar00rootroot0000000000000052 comment=432b990bc9515d434710f3e52c944aa73ddeda5b go-autorest-0.1-alpha/000077500000000000000000000000001256314551300147135ustar00rootroot00000000000000go-autorest-0.1-alpha/LICENSE000066400000000000000000000250171256314551300157250ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 2015 Microsoft Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. go-autorest-0.1-alpha/README.md000066400000000000000000000011631256314551300161730ustar00rootroot00000000000000# go-autorest [![GoDoc](https://godoc.org/github.com/azure/go-autorest/autorest?status.png)](https://godoc.org/github.com/azure/go-autorest/autorest) ## Usage ``` package main import ( "net/http" "time" "github.com/azure/go-autorest/autorest" ) func main() { req, err := Prepare(&http.Request{}, WithAuthorization()) resp, err := Send(req, WithLogging(logger), DoErrorIfStatusCode(500), DoCloseIfError(), DoRetryForAttempts(5, time.Second)) err = Respond(resp, ByClosing()) } ``` ## Install ```bash go get github.com/azure/go-autorest/autorest ``` ## License See LICENSE file. go-autorest-0.1-alpha/autorest/000077500000000000000000000000001256314551300165615ustar00rootroot00000000000000go-autorest-0.1-alpha/autorest/autorest.go000066400000000000000000000127511256314551300207640ustar00rootroot00000000000000/* This package implements an HTTP request pipeline suitable for use across multiple go-routines and provides the shared routines relied on by AutoRest (see https://github.com/azure/autorest/) generated Go code. The package breaks sending and responding to HTTP requests into three phases: Preparing, Sending, and Responding. A typical pattern is: req, err := Prepare(&http.Request{}, WithAuthorization()) resp, err := Send(req, WithLogging(logger), DoErrorIfStatusCode(500), DoCloseIfError(), DoRetryForAttempts(5, time.Second)) err = Respond(resp, ByClosing()) Each phase relies on decorators to modify and / or manage processing. Decorators may first modify and then pass the data along, pass the data first and then modify the result, or wrap themselves around passing the data (such as a logger might do). Decorators run in the order provided. For example, the following: req, err := Prepare(&http.Request{}, WithBaseURL("https://microsoft.com/"), WithPath("a"), WithPath("b"), WithPath("c")) will set the URL to: https://microsoft.com/a/b/c Preparers and Responders may be shared and re-used (assuming the underlying decorators support sharing and re-use). Performant use is obtained by creating one or more Preparers and Responders shared among multiple go-routines, and a single Sender shared among multiple sending go-routines, all bound together by means of input / output channels. Decorators hold their passed state within a closure (such as the path components in the example above). Be careful to share Preparers and Responders only in a context where such held state applies. For example, it may not make sense to share a Preparer that applies a query string from a fixed set of values. Similarly, sharing a Responder that reads the response body into a passed struct (e.g., ByUnmarshallingJson) is likely incorrect. Lastly, the Swagger specification (https://swagger.io) that drives AutoRest (https://github.com/azure/autorest/) precisely defines two date forms (i.e., date and date-time). The two sub-packages -- github.com/azure/go-autorest/autorest/date and github.com/azure/go-autorest/autorest/datetime -- provide time.Time derivations to ensure correct parsing and formatting. See the included examples for more detail. */ package autorest import ( "fmt" "net/http" "time" ) const ( headerLocation = "Location" headerRetryAfter = "Retry-After" ) // CreatePollingRequest allocates and returns a new http.Request, along with suggested a retry // delay, if the supplied http.Response code is with the passed set (the set defaults to an // HTTP 202). The http.Response must include both Location and Retry-After headers. If the passed // http.Response is to be polled, this method will close the http.Response body. A passed http.Response // with an HTTP 200 status code is ignored. func CreatePollingRequest(resp *http.Response, authorizer Authorizer, codes ...int) (*http.Request, time.Duration, error) { d := time.Duration(0) if resp.StatusCode == 200 { return nil, d, nil } if len(codes) == 0 { codes = []int{202} } if !ResponseHasStatusCode(resp, codes...) { return nil, d, fmt.Errorf("autorest: Poll requested for unexpected status code -- %v not in %v", resp.StatusCode, codes) } location := resp.Header.Get(headerLocation) if location == "" { return nil, d, fmt.Errorf("autorest: Missing Location header in poll request to %s", resp.Request.URL) } retry := resp.Header.Get(headerRetryAfter) if retry == "" { return nil, d, fmt.Errorf("autorest: Missing Retry-After header in poll request to %s", resp.Request.URL) } d, err := time.ParseDuration(retry + "s") if err != nil { return nil, d, fmt.Errorf("autorest: Failed to parse retry duration (%s) in poll request to %s -- (%v)", retry, location, err) } req, err := Prepare(&http.Request{}, AsGet(), WithBaseURL(location), authorizer.WithAuthorization()) if err != nil { return nil, d, fmt.Errorf("autorest: Failure creating poll request to %s (%v)", location, err) } Respond(resp, ByClosing()) return req, d, nil } // PollForAttempts will retry the passed http.Request until it receives an HTTP status code outside // the passed set or has made the specified number of attempts. The set of status codes defaults to // HTTP 202. func PollForAttempts(s Sender, req *http.Request, delay time.Duration, attempts int, codes ...int) (*http.Response, error) { return SendWithSender( decorateForPolling(s, delay, codes...), req, DoRetryForAttempts(attempts, time.Duration(0))) } // PollForDuration will retry the passed http.Request until it receives an HTTP status code outside // the passed set or the total time meets or exceeds the specified duration. The set of status codes // defaults to HTTP 202. func PollForDuration(s Sender, req *http.Request, delay time.Duration, total time.Duration, codes ...int) (*http.Response, error) { return SendWithSender( decorateForPolling(s, delay, codes...), req, DoRetryForDuration(total, time.Duration(0))) } func decorateForPolling(s Sender, delay time.Duration, codes ...int) Sender { if len(codes) == 0 { codes = []int{202} } return DecorateSender(s, AfterDelay(delay), DoCloseIfError(), DoErrorIfStatusCode(codes...)) } // ResponseHasStatusCode returns true if the status code in the HTTP Response is in the passed set // and false otherwise. func ResponseHasStatusCode(resp *http.Response, codes ...int) bool { return containsInt(codes, resp.StatusCode) } go-autorest-0.1-alpha/autorest/autorest_test.go000066400000000000000000000232241256314551300220200ustar00rootroot00000000000000package autorest import ( "fmt" "net/http" "regexp" "testing" "time" "github.com/azure/go-autorest/autorest/mocks" ) const ( testAuthorizationHeader = "BEARER SECRETTOKEN" testUrl = "https://microsoft.com/a/b/c/" ) func TestCreatePollingRequestIgnoresSuccess(t *testing.T) { resp := mocks.NewResponse() req, _, _ := CreatePollingRequest(resp, NullAuthorizer{}) if req != nil { t.Error("autorest: CreatePollingRequest did not ignore a successful response") } } func TestCreatePollingRequestIgnoresSuccessWithoutError(t *testing.T) { resp := mocks.NewResponse() _, _, err := CreatePollingRequest(resp, NullAuthorizer{}) if err != nil { t.Errorf("autorest: CreatePollingRequest returned an error when ignoring success (%v)", err) } } func TestCreatePollingRequestLeavesBodyOpenOfIgnoredSuccess(t *testing.T) { resp := mocks.NewResponse() req, _, _ := CreatePollingRequest(resp, NullAuthorizer{}) if req != nil { t.Error("autorest: CreatePollingRequest closed the responise body while ignoring a successful response") } } func TestCreatePollingRequestDefaultsToAcceptedStatusCode(t *testing.T) { resp := mocks.NewResponseWithStatus("202 Accepted", 202) addAcceptedHeaders(resp) req, _, _ := CreatePollingRequest(resp, NullAuthorizer{}) if req == nil { t.Error("autorest: CreatePollingRequest failed to create a request for default 202 Accepted status code") } } func TestCreatePollingRequestReturnsErrorsForUnexpectedStatusCodes(t *testing.T) { resp := mocks.NewResponseWithStatus("500 ServerError", 500) addAcceptedHeaders(resp) _, _, err := CreatePollingRequest(resp, NullAuthorizer{}) if err == nil { t.Errorf("autorest: CreatePollingRequest did not return an error when ignoring a status code (%v)", err) } } func TestCreatePollingRequestDoesNotCreateARequestForUnexpectedStatusCodes(t *testing.T) { resp := mocks.NewResponseWithStatus("500 ServerError", 500) addAcceptedHeaders(resp) req, _, _ := CreatePollingRequest(resp, NullAuthorizer{}) if req != nil { t.Error("autorest: CreatePollingRequest created a new request when ignoring a status code") } } func TestCreatePollingRequestLeavesBodyOpenOfIgnoredStatusCode(t *testing.T) { resp := mocks.NewResponseWithStatus("500 ServerError", 500) addAcceptedHeaders(resp) CreatePollingRequest(resp, NullAuthorizer{}) if !resp.Body.(*mocks.Body).IsOpen() { t.Error("autorest: CreatePollingRequest closed Body of an ignored status code") } } func TestCreatePollingRequestFailsWithoutLocationHeader(t *testing.T) { resp := mocks.NewResponseWithStatus("202 Accepted", 202) mocks.AddResponseHeader(resp, http.CanonicalHeaderKey(headerRetryAfter), "0") _, _, err := CreatePollingRequest(resp, NullAuthorizer{}) if err == nil { t.Error("autorest: CreatePollingRequest failed to detect missing Location header") } else { f, _ := regexp.MatchString(".*Missing Location header.*", fmt.Sprintf("%v", err)) if !f { t.Errorf("autorest: CreatePollingRequest returned an unexpected error (%v) for a missing Location", err) } } } func TestCreatePollingRequestFailsWithoutRetryAfterHeader(t *testing.T) { resp := mocks.NewResponseWithStatus("202 Accepted", 202) mocks.AddResponseHeader(resp, http.CanonicalHeaderKey(headerLocation), "https://microsoft.com/a/b/c/") _, _, err := CreatePollingRequest(resp, NullAuthorizer{}) if err == nil { t.Error("autorest: CreatePollingRequest failed to detect missing Retry-After header") } else { f, _ := regexp.MatchString(".*Missing Retry-After header.*", fmt.Sprintf("%v", err)) if !f { t.Errorf("autorest: CreatePollingRequest returned an unexpected error (%v) for a missing Retry-After", err) } } } func TestCreatePollingRequestClosesTheResponseBody(t *testing.T) { resp := mocks.NewResponseWithStatus("202 Accepted", 202) addAcceptedHeaders(resp) CreatePollingRequest(resp, NullAuthorizer{}) if resp.Body.(*mocks.Body).IsOpen() { t.Error("autorest: CreatePollingRequest failed to close the response body when creating a new request") } } func TestCreatePollingRequestReturnsAGetRequest(t *testing.T) { resp := mocks.NewResponseWithStatus("202 Accepted", 202) addAcceptedHeaders(resp) req, _, _ := CreatePollingRequest(resp, NullAuthorizer{}) if req.Method != "GET" { t.Errorf("autorest: CreatePollingRequest did not create an HTTP GET request -- actual method %v", req.Method) } } func TestCreatePollingRequestProvidesTheURL(t *testing.T) { resp := mocks.NewResponseWithStatus("202 Accepted", 202) addAcceptedHeaders(resp) req, _, _ := CreatePollingRequest(resp, NullAuthorizer{}) if req.URL.String() != testUrl { t.Errorf("autorest: CreatePollingRequest did not create an HTTP with the expected URL -- received %v, expected %v", req.URL, testUrl) } } func TestCreatePollingRequestAppliesAuthorization(t *testing.T) { resp := mocks.NewResponseWithStatus("202 Accepted", 202) addAcceptedHeaders(resp) req, _, _ := CreatePollingRequest(resp, TestAuthorizer{}) if req.Header.Get(http.CanonicalHeaderKey(headerAuthorization)) != testAuthorizationHeader { t.Errorf("autorest: CreatePollingRequest did not apply authorization -- received %v, expected %v", req.Header.Get(http.CanonicalHeaderKey(headerAuthorization)), testAuthorizationHeader) } } func TestPollForAttemptsStops(t *testing.T) { client := mocks.NewClient() client.EmitErrors(-1) resp := mocks.NewResponseWithStatus("202 Accepted", 202) addAcceptedHeaders(resp) req, _, _ := CreatePollingRequest(resp, NullAuthorizer{}) PollForAttempts(client, req, time.Duration(0), 5) if client.Attempts() < 5 || client.Attempts() > 5 { t.Errorf("autorest: PollForAttempts stopped incorrectly -- expected %v attempts, actual attempts were %v", 5, client.Attempts()) } } func TestPollForDurationsStops(t *testing.T) { client := mocks.NewClient() client.EmitErrors(-1) resp := mocks.NewResponseWithStatus("202 Accepted", 202) addAcceptedHeaders(resp) req, _, _ := CreatePollingRequest(resp, NullAuthorizer{}) d := 10 * time.Millisecond start := time.Now() PollForDuration(client, req, time.Duration(0), d) if time.Now().Sub(start) < d { t.Error("autorest: PollForDuration stopped too soon") } } func TestPollForDurationsStopsWithinReason(t *testing.T) { client := mocks.NewClient() client.EmitErrors(-1) resp := mocks.NewResponseWithStatus("202 Accepted", 202) addAcceptedHeaders(resp) req, _, _ := CreatePollingRequest(resp, NullAuthorizer{}) d := 10 * time.Millisecond start := time.Now() PollForDuration(client, req, time.Duration(0), d) if time.Now().Sub(start) > (time.Duration(5.0) * d) { t.Error("autorest: PollForDuration took too long to stop -- exceeded 5 times expected duration") } } func TestPollingHonorsDelay(t *testing.T) { client := mocks.NewClient() client.EmitErrors(-1) resp := mocks.NewResponseWithStatus("202 Accepted", 202) addAcceptedHeaders(resp) req, _, _ := CreatePollingRequest(resp, NullAuthorizer{}) d1 := 10 * time.Millisecond start := time.Now() PollForAttempts(client, req, d1, 1) d2 := time.Now().Sub(start) if d2 < d1 { t.Errorf("autorest: Polling failed to honor delay -- expected %v, actual %v", d1.Seconds(), d2.Seconds()) } } func TestPollingReturnsErrorForExpectedStatusCode(t *testing.T) { client := mocks.NewClient() client.EmitStatus("202 Accepted", 202) resp := mocks.NewResponseWithStatus("202 Accepted", 202) addAcceptedHeaders(resp) req, _, _ := CreatePollingRequest(resp, NullAuthorizer{}) resp, err := PollForAttempts(client, req, time.Duration(0), 1, 202) if err == nil { t.Error("autorest: doPoll failed to emit error for known status code") } } func TestPollingReturnsNoErrorForUnexpectedStatusCode(t *testing.T) { client := mocks.NewClient() client.EmitStatus("500 ServerError", 500) resp := mocks.NewResponseWithStatus("202 Accepted", 202) addAcceptedHeaders(resp) req, _, _ := CreatePollingRequest(resp, NullAuthorizer{}) resp, err := PollForAttempts(client, req, time.Duration(0), 1, 202) if err != nil { t.Error("autorest: doPoll emitted error for unknown status code") } } func TestPollingReturnsDefaultsToAcceptedStatusCode(t *testing.T) { client := mocks.NewClient() client.EmitStatus("202 Accepted", 202) resp := mocks.NewResponseWithStatus("202 Accepted", 202) addAcceptedHeaders(resp) req, _, _ := CreatePollingRequest(resp, NullAuthorizer{}) resp, err := PollForAttempts(client, req, time.Duration(0), 1) if err == nil { t.Error("autorest: doPoll failed to default to HTTP 202") } } func TestdoPollLeavesFinalBodyOpen(t *testing.T) { client := mocks.NewClient() client.EmitStatus("500 ServerError", 500) resp := mocks.NewResponseWithStatus("202 Accepted", 202) addAcceptedHeaders(resp) req, _, _ := CreatePollingRequest(resp, NullAuthorizer{}) resp, _ = PollForAttempts(client, req, time.Duration(0), 1) if !resp.Body.(*mocks.Body).IsOpen() { t.Error("autorest: doPoll unexpectedly closed the response body") } } func TestResponseHasStatusCode(t *testing.T) { codes := []int{200, 202} resp := &http.Response{StatusCode: 202} if !ResponseHasStatusCode(resp, codes...) { t.Errorf("autorest: ResponseHasStatusCode failed to find %v in %v", resp.StatusCode, codes) } } func TestResponseHasStatusCodeNotPresent(t *testing.T) { codes := []int{200, 202} resp := &http.Response{StatusCode: 500} if ResponseHasStatusCode(resp, codes...) { t.Errorf("autorest: ResponseHasStatusCode unexpectedly found %v in %v", resp.StatusCode, codes) } } func addAcceptedHeaders(resp *http.Response) { mocks.AddResponseHeader(resp, http.CanonicalHeaderKey(headerLocation), testUrl) mocks.AddResponseHeader(resp, http.CanonicalHeaderKey(headerRetryAfter), "0") } type TestAuthorizer struct{} func (ta TestAuthorizer) WithAuthorization() PrepareDecorator { return WithHeader(headerAuthorization, testAuthorizationHeader) } go-autorest-0.1-alpha/autorest/azure/000077500000000000000000000000001256314551300177075ustar00rootroot00000000000000go-autorest-0.1-alpha/autorest/azure/azure.go000066400000000000000000000002171256314551300213640ustar00rootroot00000000000000/* This package provides Azure-specific implementations used with AutoRest. See the included examples for more detail. */ package azure go-autorest-0.1-alpha/autorest/azure/token.go000066400000000000000000000125441256314551300213640ustar00rootroot00000000000000package azure import ( "fmt" "net/http" "net/url" "strconv" "time" "github.com/azure/go-autorest/autorest" ) const ( defaultRefresh = 5 * time.Minute oauthUrl = "https://login.microsoftonline.com/{tenantId}/oauth2/{requestType}?api-version=1.0" tokenBaseDate = "1970-01-01T00:00:00Z" ) var expirationBase time.Time func init() { expirationBase, _ = time.Parse(time.RFC3339, tokenBaseDate) } // Token encapsulates the access token used to authorize Azure requests. type Token struct { AccessToken string `json:"access_token"` ExpiresIn string `json:"expires_in"` ExpiresOn string `json:"expires_on"` NotBefore string `json:"not_before"` Resource string `json:"resource"` Type string `json:"token_type"` } // Expires returns the time.Time when the Token expires. func (t Token) Expires() time.Time { s, err := strconv.Atoi(t.ExpiresOn) if err != nil { s = -3600 } return expirationBase.Add(time.Duration(s) * time.Second).UTC() } // IsExpired returns true if the Token is expired, false otherwise. func (t Token) IsExpired() bool { return t.WillExpireIn(0) } // WillExpireIn returns true if the Token will expire after the passed time.Duration interval // from now, false otherwise. func (t Token) WillExpireIn(d time.Duration) bool { return !t.Expires().After(time.Now().Add(d)) } // WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose // value is "Bearer " followed by the AccessToken of the Token. func (t *Token) WithAuthorization() autorest.PrepareDecorator { return func(p autorest.Preparer) autorest.Preparer { return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { return (autorest.WithBearerAuthorization(t.AccessToken)(p)).Prepare(r) }) } } // ServicePrincipalToken encapsulates a Token created for a Service Principal. type ServicePrincipalToken struct { Token clientId string clientSecret string resource string tenantId string autoRefresh bool refreshWithin time.Duration sender autorest.Sender } // NewTokenForServicePrincipal creates a ServicePrincipalToken from the supplied Service Principal // credentials scoped to the named resource. func NewServicePrincipalToken(id string, secret string, tenentId string, resource string) (*ServicePrincipalToken, error) { spt := &ServicePrincipalToken{ clientId: id, clientSecret: secret, resource: resource, tenantId: tenentId, autoRefresh: true, refreshWithin: defaultRefresh, sender: &http.Client{}} return spt, nil } // EnsureFresh will refresh the token if it will expire within the refresh window (as set by // RefreshWithin). func (spt *ServicePrincipalToken) EnsureFresh() error { if spt.WillExpireIn(spt.refreshWithin) { return spt.Refresh() } return nil } // Refresh obtains a fresh token for the Service Principal. func (spt *ServicePrincipalToken) Refresh() error { p := map[string]interface{}{ "tenantId": spt.tenantId, "requestType": "token", } v := url.Values{} v.Set("client_id", spt.clientId) v.Set("client_secret", spt.clientSecret) v.Set("grant_type", "client_credentials") v.Set("resource", spt.resource) req, err := autorest.Prepare(&http.Request{}, autorest.AsPost(), autorest.AsFormUrlEncoded(), autorest.WithBaseURL(oauthUrl), autorest.WithPathParameters(p), autorest.WithFormData(v)) if err != nil { return fmt.Errorf("azure: Failed to create refresh request for Service Principal %s (%v)", spt.clientId, err) } resp, err := autorest.SendWithSender(spt.sender, req) if err != nil { return fmt.Errorf("azure: Token request for Service Principal %s failed (%v)", spt.clientId, err) } err = autorest.Respond(resp, autorest.WithErrorUnlessOK(), autorest.ByUnmarshallingJSON(spt), autorest.ByClosing()) if err != nil { return fmt.Errorf("azure: Token request for Service Principal %s returned an unexpected error (%v)", spt.clientId, err) } return nil } // SetAutoRefresh enables or disables automatic refreshing of stale tokens. func (spt *ServicePrincipalToken) SetAutoRefresh(autoRefresh bool) { spt.autoRefresh = autoRefresh } // SetRefreshWithin sets the interval within which if the token will expire, EnsureFresh will // refresh the token. func (spt *ServicePrincipalToken) SetRefreshWithin(d time.Duration) { spt.refreshWithin = d return } // SetSender sets the autorest.Sender used when obtaining the Service Principal token. An // undecorated http.Client is used by default. func (spt *ServicePrincipalToken) SetSender(s autorest.Sender) { spt.sender = s } // WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose // value is "Bearer " followed by the AccessToken of the ServicePrincipalToken. // // By default, the token will automatically refresh if nearly expired (as determined by the // RefreshWithin interval). Use the AutoRefresh method to enable or disable automatically refreshing // tokens. func (spt *ServicePrincipalToken) WithAuthorization() autorest.PrepareDecorator { return func(p autorest.Preparer) autorest.Preparer { return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { if spt.autoRefresh { err := spt.EnsureFresh() if err != nil { return r, fmt.Errorf("azure: Failed to refresh Service Principal Token for request to %s (%v)", r.URL, err) } } return (autorest.WithBearerAuthorization(spt.AccessToken)(p)).Prepare(r) }) } } go-autorest-0.1-alpha/autorest/azure/token_test.go000066400000000000000000000216431256314551300224230ustar00rootroot00000000000000package azure import ( "fmt" "io/ioutil" "net/http" "reflect" "strconv" "testing" "time" "github.com/azure/go-autorest/autorest" "github.com/azure/go-autorest/autorest/mocks" ) const ( defaultFormData = "client_id=id&client_secret=secret&grant_type=client_credentials&resource=resource" ) func TestTokenExpires(t *testing.T) { tt := time.Now().Add(5 * time.Second) tk := newTokenExpiresAt(tt) if tk.Expires().Equal(tt) { t.Errorf("azure: Token miscalculated expiration time -- received %v, expected %v", tk.Expires(), tt) } } func TestTokenIsExpired(t *testing.T) { tk := newTokenExpiresAt(time.Now().Add(-5 * time.Second)) if !tk.IsExpired() { t.Errorf("azure: Token failed to mark a stale token as expired -- now %v, token expires at %v", time.Now().UTC(), tk.Expires()) } } func TestTokenIsExpiredUninitialized(t *testing.T) { tk := &Token{} if !tk.IsExpired() { t.Errorf("azure: An uninitialized Token failed to mark itself as expired (expiration time %v)", tk.Expires()) } } func TestTokenIsNoExpired(t *testing.T) { tk := newTokenExpiresAt(time.Now().Add(1000 * time.Second)) if tk.IsExpired() { t.Errorf("azure: Token marked a fresh token as expired -- now %v, token expires at %v", time.Now().UTC(), tk.Expires()) } } func TestTokenWillExpireIn(t *testing.T) { d := 5 * time.Second tk := newTokenExpiresIn(d) if !tk.WillExpireIn(d) { t.Error("azure: Token mismeasured expiration time") } } func TestTokenWithAuthorization(t *testing.T) { tk := newToken() req, err := autorest.Prepare(&http.Request{}, tk.WithAuthorization()) if err != nil { t.Errorf("azure: Token WithAuthorization returned an error (%v)", err) } else if req.Header.Get(http.CanonicalHeaderKey("Authorization")) != fmt.Sprintf("Bearer %s", tk.AccessToken) { t.Error("azure: Token WithAuthorization failed to set Authorization header") } } func TestServicePrincipalTokenSetAutoRefresh(t *testing.T) { spt := newServicePrincipalToken() if !spt.autoRefresh { t.Error("azure: ServicePrincipalToken did not default to automatic token refreshing") } spt.SetAutoRefresh(false) if spt.autoRefresh { t.Error("azure: ServicePrincipalToken#SetAutoRefresh did not disable automatic token refreshing") } } func TestServicePrincipalTokenSetRefreshWithin(t *testing.T) { spt := newServicePrincipalToken() if spt.refreshWithin != defaultRefresh { t.Error("azure: ServicePrincipalToken did not correctly set the default refresh interval") } spt.SetRefreshWithin(2 * defaultRefresh) if spt.refreshWithin != 2*defaultRefresh { t.Error("azure: ServicePrincipalToken#SetRefreshWithin did not set the refresh interval") } } func TestServicePrincipalTokenSetSender(t *testing.T) { spt := newServicePrincipalToken() var s autorest.Sender s = mocks.NewClient() spt.SetSender(s) if !reflect.DeepEqual(s, spt.sender) { t.Error("azure: ServicePrincipalToken#SetSender did not set the sender") } } func TestServicePrincipalTokenRefreshUsesPOST(t *testing.T) { spt := newServicePrincipalToken() c := mocks.NewClient() s := autorest.DecorateSender(c, (func() autorest.SendDecorator { return func(s autorest.Sender) autorest.Sender { return autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { if r.Method != "POST" { t.Errorf("azure: ServicePrincipalToken#Refresh did not correctly set HTTP method -- expected %v, received %v", "POST", r.Method) } return mocks.NewResponse(), nil }) } })()) spt.SetSender(s) spt.Refresh() } func TestServicePrincipalTokenRefreshSetsMimeType(t *testing.T) { spt := newServicePrincipalToken() c := mocks.NewClient() s := autorest.DecorateSender(c, (func() autorest.SendDecorator { return func(s autorest.Sender) autorest.Sender { return autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { if r.Header.Get(http.CanonicalHeaderKey("Content-Type")) != "application/x-www-form-urlencoded" { t.Errorf("azure: ServicePrincipalToken#Refresh did not correctly set Content-Type -- expected %v, received %v", "application/x-form-urlencoded", r.Header.Get(http.CanonicalHeaderKey("Content-Type"))) } return mocks.NewResponse(), nil }) } })()) spt.SetSender(s) spt.Refresh() } func TestServicePrincipalTokenRefreshSetsURL(t *testing.T) { spt := newServicePrincipalToken() c := mocks.NewClient() s := autorest.DecorateSender(c, (func() autorest.SendDecorator { return func(s autorest.Sender) autorest.Sender { return autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { u := fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/token?api-version=1.0", spt.tenantId) if r.URL.String() != u { t.Errorf("azure: ServicePrincipalToken#Refresh did not correctly set the URL -- expected %v, received %v", u, r.URL) } return mocks.NewResponse(), nil }) } })()) spt.SetSender(s) spt.Refresh() } func TestServicePrincipalTokenRefreshSetsBody(t *testing.T) { spt := newServicePrincipalToken() c := mocks.NewClient() s := autorest.DecorateSender(c, (func() autorest.SendDecorator { return func(s autorest.Sender) autorest.Sender { return autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { b, err := ioutil.ReadAll(r.Body) if err != nil { t.Errorf("azure: Failed to read body of Service Principal token request (%v)", err) } else if string(b) != defaultFormData { t.Errorf("azure: ServicePrincipalToken#Refresh did not correctly set the HTTP Request Body -- expected %v, received %v", defaultFormData, string(b)) } return mocks.NewResponse(), nil }) } })()) spt.SetSender(s) spt.Refresh() } func TestServicePrincipalTokenRefreshClosesRequestBody(t *testing.T) { spt := newServicePrincipalToken() resp := mocks.NewResponse() c := mocks.NewClient() s := autorest.DecorateSender(c, (func() autorest.SendDecorator { return func(s autorest.Sender) autorest.Sender { return autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { return resp, nil }) } })()) spt.SetSender(s) spt.Refresh() if resp.Body.(*mocks.Body).IsOpen() { t.Error("azure: ServicePrincipalToken#Refresh failed to close the HTTP Response Body") } } func TestServicePrincipalTokenRefreshPropagatesErrors(t *testing.T) { spt := newServicePrincipalToken() c := mocks.NewClient() c.EmitErrors(1) spt.SetSender(c) err := spt.Refresh() if err == nil { t.Error("azure: Failed to propagate the request error") } } func TestServicePrincipalTokenRefreshReturnsErrorIfNotOk(t *testing.T) { spt := newServicePrincipalToken() c := mocks.NewClient() c.EmitStatus("401 NotAuthorized", 401) spt.SetSender(c) err := spt.Refresh() if err == nil { t.Error("azure: Failed to return an when receiving a status code other than HTTP 200") } } func TestServicePrincipalTokenRefreshUnmarshals(t *testing.T) { spt := newServicePrincipalToken() expiresOn := strconv.Itoa(int(time.Now().Add(3600 * time.Second).Sub(expirationBase).Seconds())) j := newTokenJSON(expiresOn, "resource") resp := mocks.NewResponseWithContent(j) c := mocks.NewClient() s := autorest.DecorateSender(c, (func() autorest.SendDecorator { return func(s autorest.Sender) autorest.Sender { return autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { return resp, nil }) } })()) spt.SetSender(s) err := spt.Refresh() if err != nil { t.Errorf("azure: ServicePrincipalToken#Refresh returned an unexpected error (%v)", err) } else if spt.AccessToken != "accessToken" || spt.ExpiresIn != "3600" || spt.ExpiresOn != expiresOn || spt.NotBefore != expiresOn || spt.Resource != "resource" || spt.Type != "Bearer" { t.Errorf("azure: ServicePrincipalToken#Refresh failed correctly unmarshal the JSON -- expected %v, received %v", j, *spt) } } func newToken() *Token { return &Token{ AccessToken: "ASECRETVALUE", Resource: "https://azure.microsoft.com/", Type: "Bearer", } } func newTokenJSON(expiresOn string, resource string) string { return fmt.Sprintf(`{ "access_token" : "accessToken", "expires_in" : "3600", "expires_on" : "%s", "not_before" : "%s", "resource" : "%s", "token_type" : "Bearer" }`, expiresOn, expiresOn, resource) } func newTokenExpiresIn(expireIn time.Duration) *Token { return setTokenToExpireIn(newToken(), expireIn) } func newTokenExpiresAt(expireAt time.Time) *Token { return setTokenToExpireAt(newToken(), expireAt) } func expireToken(t *Token) *Token { return setTokenToExpireIn(t, 0) } func setTokenToExpireAt(t *Token, expireAt time.Time) *Token { t.ExpiresIn = "3600" t.ExpiresOn = strconv.Itoa(int(expireAt.Sub(expirationBase).Seconds())) t.NotBefore = t.ExpiresOn return t } func setTokenToExpireIn(t *Token, expireIn time.Duration) *Token { return setTokenToExpireAt(t, time.Now().Add(expireIn)) } func newServicePrincipalToken() *ServicePrincipalToken { spt, _ := NewServicePrincipalToken("id", "secret", "tenentId", "resource") return spt } go-autorest-0.1-alpha/autorest/client.go000066400000000000000000000104631256314551300203720ustar00rootroot00000000000000package autorest import ( "fmt" "net/http" "time" ) // PollingMode sets how, if at all, clients composed with Client will poll. type PollingMode string const ( // Poll until reaching a maximum number of attempts PollUntilAttempts PollingMode = "poll-until-attempts" // Poll until a specified time.Duration has passed PollUntilDuration PollingMode = "poll-until-duration" // Do not poll at all DoNotPoll PollingMode = "not-at-all" ) // RequestInspector defines a single method that returns a PrepareDecorator used to inspect the // http.Request prior to sending. type RequestInspector interface { WithInspection() PrepareDecorator } // ResponseInspector defines a single method that returns a ResponseDecorator used to inspect the // http.Response prior to responding. type ResponseInspector interface { ByInspecting() RespondDecorator } // Client is a convenience base for autorest generated clients. It provides default, "do nothing" // implementations of an Authorizer, RequestInspector, and ResponseInspector. It also returns the // standard, undecorated http.Client as a default Sender. Lastly, it supports basic request polling, // limited to a maximum number of attempts or a specified duration. // // Most uses of generated clients can customize behavior or inspect requests through by supplying a // custom Authorizer, custom RequestInspector, and / or custom ResponseInspector. Users may log // requests, implement circuit breakers (see https://msdn.microsoft.com/en-us/library/dn589784.aspx) // or otherwise influence sending the request by providing a decorated Sender. type Client struct { Authorizer Authorizer Sender Sender RequestInspector RequestInspector ResponseInspector ResponseInspector PollingMode PollingMode PollingAttempts int PollingDuration time.Duration } // PollIfNeeded is a convenience method that will poll if the passed http.Response requires it. func (c *Client) PollIfNeeded(resp *http.Response, codes ...int) (*http.Response, error) { req, delay, err := CreatePollingRequest(resp, c, codes...) if err != nil { return resp, fmt.Errorf("autorest: Failed to create Poll request (%v)", err) } if req == nil { return resp, nil } if c.PollForAttempts() { return PollForAttempts(c, req, delay, c.PollingAttempts, codes...) } else if c.PollForDuration() { return PollForDuration(c, req, delay, c.PollingDuration, codes...) } else { return resp, fmt.Errorf("autorest: Polling for %s is required, but polling is disabled", req.URL) } } // DoNotPoll returns true if the client should not poll, false otherwise. func (c Client) DoNotPoll() bool { return len(c.PollingMode) == 0 || c.PollingMode == DoNotPoll } // PollForAttempts returns true if the PollingMode is set to ForAttempts, false otherwise. func (c Client) PollForAttempts() bool { return c.PollingMode == PollUntilAttempts } // PollForDuration return true if the PollingMode is set to ForDuration, false otherwise. func (c Client) PollForDuration() bool { return c.PollingMode == PollUntilDuration } // Do is a convenience method that invokes the Sender of the Client. It will use the default // http.Client if no Sender is set. func (c *Client) Do(r *http.Request) (*http.Response, error) { if c.Sender == nil { c.Sender = &http.Client{} } return c.Sender.Do(r) } // WithAuthorization is a convenience method that returns the WithAuthorization PrepareDecorator // from the current Authorizer. If not Authorizer is set, it returns the WithAuthorization // PrepareDecorator from the NullAuthorizer. func (c *Client) WithAuthorization() PrepareDecorator { if c.Authorizer == nil { c.Authorizer = NullAuthorizer{} } return c.Authorizer.WithAuthorization() } // WithInspection is a convenience method that passes the request to the supplied RequestInspector, // if present, or returns the WithNothing PrepareDecorator otherwise. func (c *Client) WithInspection() PrepareDecorator { if c.RequestInspector == nil { return WithNothing() } else { return c.RequestInspector.WithInspection() } } // ByInspecting is a convenience method that passes the response to the supplied ResponseInspector, // if present, or returns the ByIgnoring RespondDecorator otherwise. func (c *Client) ByInspecting() RespondDecorator { if c.ResponseInspector == nil { return ByIgnoring() } else { return c.ResponseInspector.ByInspecting() } } go-autorest-0.1-alpha/autorest/client_test.go000066400000000000000000000134121256314551300214260ustar00rootroot00000000000000package autorest import ( "net/http" "reflect" "testing" "time" "github.com/azure/go-autorest/autorest/mocks" ) const ( headerMockAuthorizer = "x-mock-authorizer" ) func TestClientPollIfNeededIgnoresOk(t *testing.T) { c := &Client{} s := mocks.NewClient() c.Sender = s r := mocks.NewResponse() resp, err := c.PollIfNeeded(r) if err != nil { t.Errorf("autorest: Client#PollIfNeeded failed when given a successful HTTP request (%v)", err) } if s.Attempts() > 0 { t.Error("autorest: Client#PollIfNeeded attempted to poll a successful HTTP request") } Respond(resp, ByClosing()) } func TestClientPollIfNeededLeavesBodyOpen(t *testing.T) { c := &Client{} c.Sender = mocks.NewClient() r := mocks.NewResponse() resp, err := c.PollIfNeeded(r) if err != nil { t.Errorf("autorest: Client#PollIfNeeded failed when given a successful HTTP request (%v)", err) } if !resp.Body.(*mocks.Body).IsOpen() { t.Error("autorest: Client#PollIfNeeded unexpectedly closed the response body") } Respond(resp, ByClosing()) } func TestClientPollIfNeededPollsForAttempts(t *testing.T) { c := &Client{} c.PollingMode = PollUntilAttempts c.PollingAttempts = 5 s := mocks.NewClient() s.EmitStatus("202 Accepted", 202) c.Sender = s r := mocks.NewResponseWithStatus("202 Accepted", 202) addAcceptedHeaders(r) resp, _ := c.PollIfNeeded(r) if s.Attempts() != 5 { t.Errorf("autorest: Client#PollIfNeeded did not poll the expected number of attempts -- expected %v, actual %v", c.PollingAttempts, s.Attempts()) } Respond(resp, ByClosing()) } func TestClientPollIfNeededPollsForDuration(t *testing.T) { c := &Client{} c.PollingMode = PollUntilDuration c.PollingDuration = 10 * time.Millisecond s := mocks.NewClient() s.EmitStatus("202 Accepted", 202) c.Sender = s r := mocks.NewResponseWithStatus("202 Accepted", 202) addAcceptedHeaders(r) d1 := 10 * time.Millisecond start := time.Now() resp, _ := c.PollIfNeeded(r) d2 := time.Now().Sub(start) if d2 < d1 { t.Errorf("autorest: Client#PollIfNeeded did not poll for the expected duration -- expected %v, actual %v", d1.Seconds(), d2.Seconds()) } Respond(resp, ByClosing()) } func TestClientDoNotPoll(t *testing.T) { c := &Client{} if !c.DoNotPoll() { t.Errorf("autorest: Client requested polling by default, expected no polling (%v)", c.PollingMode) } } func TestClientDoNotPollForAttempts(t *testing.T) { c := &Client{} c.PollingMode = PollUntilAttempts if c.DoNotPoll() { t.Errorf("autorest: Client failed to request polling after polling mode set to %s", c.PollingMode) } } func TestClientDoNotPollForDuration(t *testing.T) { c := &Client{} c.PollingMode = PollUntilDuration if c.DoNotPoll() { t.Errorf("autorest: Client failed to request polling after polling mode set to %s", c.PollingMode) } } func TestClientPollForAttempts(t *testing.T) { c := &Client{} c.PollingMode = PollUntilAttempts if !c.PollForAttempts() { t.Errorf("autorest: Client#SetPollingMode failed to set polling by attempts -- polling mode set to %s", c.PollingMode) } } func TestClientPollForDuration(t *testing.T) { c := &Client{} c.PollingMode = PollUntilDuration if !c.PollForDuration() { t.Errorf("autorest: Client#SetPollingMode failed to set polling for duration -- polling mode set to %s", c.PollingMode) } } func TestClientDoSetsDefaultSender(t *testing.T) { c := &Client{} c.Do(&http.Request{}) if !reflect.DeepEqual(c.Sender, &http.Client{}) { t.Error("autorest: Client#Do failed to set the default Sender to the http.Client") } } func TestClientWithAuthorizer(t *testing.T) { c := &Client{} c.Authorizer = mockAuthorizer{} req, _ := Prepare(&http.Request{}, c.WithAuthorization()) if req.Header.Get(headerMockAuthorizer) == "" { t.Error("autorest: Client#WithAuthorizer failed to return the WithAuthorizer from the active Authorizer") } } func TestClientWithAuthorizerSetsDefaultAuthorizer(t *testing.T) { c := &Client{} Prepare(&http.Request{}, c.WithAuthorization()) if !reflect.DeepEqual(c.Authorizer, NullAuthorizer{}) { t.Error("autorest: Client#WithAuthorizer failed to set the default Authorizer to the NullAuthorizer") } } func TestClientWithInspection(t *testing.T) { c := &Client{} r := &mockInspector{} c.RequestInspector = r Prepare(&http.Request{}, c.WithInspection()) if !r.wasInvoked { t.Error("autorest: Client#WithInspection failed to invoke RequestInspector") } } func TestClientWithInspectionSetsDefault(t *testing.T) { c := &Client{} r1 := &http.Request{} r2, _ := Prepare(r1, c.WithInspection()) if !reflect.DeepEqual(r1, r2) { t.Error("autorest: Client#WithInspection failed to provide a default RequestInspector") } } func TestClientByInspecting(t *testing.T) { c := &Client{} r := &mockInspector{} c.ResponseInspector = r Respond(&http.Response{}, c.ByInspecting()) if !r.wasInvoked { t.Error("autorest: Client#ByInspecting failed to invoke ResponseInspector") } } func TestClientByInspectingSetsDefault(t *testing.T) { c := &Client{} r := &http.Response{} Respond(r, c.ByInspecting()) if !reflect.DeepEqual(r, &http.Response{}) { t.Error("autorest: Client#ByInspecting failed to provide a default ResponseInspector") } } type mockAuthorizer struct{} func (ma mockAuthorizer) WithAuthorization() PrepareDecorator { return WithHeader(headerMockAuthorizer, "authorized") } type mockInspector struct { wasInvoked bool } func (mi *mockInspector) WithInspection() PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { mi.wasInvoked = true return p.Prepare(r) }) } } func (mi *mockInspector) ByInspecting() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { mi.wasInvoked = true return r.Respond(resp) }) } } go-autorest-0.1-alpha/autorest/date/000077500000000000000000000000001256314551300174765ustar00rootroot00000000000000go-autorest-0.1-alpha/autorest/date/date.go000066400000000000000000000044771256314551300207560ustar00rootroot00000000000000/* This package provides time.Time derivatives that conform to the Swagger.io (https://swagger.io/) defined date formats: Date and DateTime. Both types may, in most cases, be used in lieu of time.Time types. And both convert to time.Time through a ToTime method. */ package date import ( "fmt" "time" ) const ( RFC3339FullDate = "2006-01-02" dateFormat = "%4d-%02d-%02d" jsonFormat = `"%4d-%02d-%02d"` ) // Defines a type similar to time.Time but assumes a layout of RFC3339 full-date (i.e., 2006-01-02). type Date struct { time.Time } // Create a new Date from the passed string. func ParseDate(date string) (d Date, err error) { d = Date{} d.Time, err = time.Parse(RFC3339FullDate, date) return d, err } // Preserves the Date as a byte array conforming to RFC3339 full-date (i.e., 2006-01-02). func (d Date) MarshalBinary() ([]byte, error) { return d.MarshalText() } // Reconstitutes a Date saved as a byte array conforming to RFC3339 full-date (i.e., 2006-01-02). func (d *Date) UnmarshalBinary(data []byte) error { return d.UnmarshalText(data) } // Preserves the Date as a JSON string conforming to RFC3339 full-date (i.e., 2006-01-02). func (d Date) MarshalJSON() (json []byte, err error) { return []byte(fmt.Sprintf(jsonFormat, d.Year(), d.Month(), d.Day())), nil } // Reconstitutes the Date from a JSON string conforming to RFC3339 full-date (i.e., 2006-01-02). func (d *Date) UnmarshalJSON(data []byte) (err error) { if data[0] == '"' { data = data[1 : len(data)-1] } d.Time, err = time.Parse(RFC3339FullDate, string(data)) if err != nil { return err } return nil } // Preserves the Date as a byte array conforming to RFC3339 full-date (i.e., 2006-01-02). func (d Date) MarshalText() (text []byte, err error) { return []byte(fmt.Sprintf(dateFormat, d.Year(), d.Month(), d.Day())), nil } // Reconstitutes a Date saved as a byte array conforming to RFC3339 full-date (i.e., 2006-01-02). func (d *Date) UnmarshalText(data []byte) (err error) { d.Time, err = time.Parse(RFC3339FullDate, string(data)) if err != nil { return err } return nil } // Returns the Date formatted as an RFC3339 full-date string (i.e., 2006-01-02). func (d Date) String() string { return fmt.Sprintf(dateFormat, d.Year(), d.Month(), d.Day()) } // Returns a Date as a time.Time func (d Date) ToTime() time.Time { return d.Time } go-autorest-0.1-alpha/autorest/date/date_test.go000066400000000000000000000064061256314551300220070ustar00rootroot00000000000000package date import ( "encoding/json" "fmt" "reflect" "testing" "time" ) func ExampleParseDate() { d, _ := ParseDate("2001-02-03") fmt.Println(d) // Output: 2001-02-03 } func ExampleDate() { d, _ := ParseDate("2001-02-03") t, _ := time.Parse(time.RFC3339, "2001-02-04T00:00:00Z") // Date acts as time.Time when the receiver if d.Before(t) { fmt.Printf("Before ") } else { fmt.Printf("After ") } // Convert Date when needing a time.Time if t.After(d.ToTime()) { fmt.Printf("After") } else { fmt.Printf("Before") } // Output: Before After } func ExampleDate_MarshalBinary() { d, _ := ParseDate("2001-02-03") t, _ := d.MarshalBinary() fmt.Println(string(t)) // Output: 2001-02-03 } func ExampleDate_UnmarshalBinary() { d := Date{} t := "2001-02-03" err := d.UnmarshalBinary([]byte(t)) if err != nil { fmt.Println(err) } fmt.Println(d) // Output: 2001-02-03 } func ExampleDate_MarshalJSON() { d, _ := ParseDate("2001-02-03") j, _ := json.Marshal(d) fmt.Println(string(j)) // Output: "2001-02-03" } func ExampleDate_UnmarshalJSON() { var d struct { Date Date `json:"date"` } j := `{ "date" : "2001-02-03" }` err := json.Unmarshal([]byte(j), &d) if err != nil { fmt.Println(err) } fmt.Println(d.Date) // Output: 2001-02-03 } func ExampleDate_MarshalText() { d, _ := ParseDate("2001-02-03") t, _ := d.MarshalText() fmt.Println(string(t)) // Output: 2001-02-03 } func ExampleDate_UnmarshalText() { d := Date{} t := "2001-02-03" err := d.UnmarshalText([]byte(t)) if err != nil { fmt.Println(err) } fmt.Println(d) // Output: 2001-02-03 } func TestDateString(t *testing.T) { d, _ := ParseDate("2001-02-03") if d.String() != "2001-02-03" { t.Errorf("date: String failed (%v)", d.String()) } } func TestDateBinaryRoundTrip(t *testing.T) { d1, err := ParseDate("2001-02-03") t1, err := d1.MarshalBinary() if err != nil { t.Errorf("date: MarshalBinary failed (%v)", err) } d2 := Date{} err = d2.UnmarshalBinary(t1) if err != nil { t.Errorf("date: UnmarshalBinary failed (%v)", err) } if !reflect.DeepEqual(d1, d2) { t.Errorf("date: Round-trip Binary failed (%v, %v)", d1, d2) } } func TestDateJSONRoundTrip(t *testing.T) { type s struct { Date Date `json:"date"` } var err error d1 := s{} d1.Date, err = ParseDate("2001-02-03") j, err := json.Marshal(d1) if err != nil { t.Errorf("date: MarshalJSON failed (%v)", err) } d2 := s{} err = json.Unmarshal(j, &d2) if err != nil { t.Errorf("date: UnmarshalJSON failed (%v)", err) } if !reflect.DeepEqual(d1, d2) { t.Errorf("date: Round-trip JSON failed (%v, %v)", d1, d2) } } func TestDateTextRoundTrip(t *testing.T) { d1, err := ParseDate("2001-02-03") t1, err := d1.MarshalText() if err != nil { t.Errorf("date: MarshalText failed (%v)", err) } d2 := Date{} err = d2.UnmarshalText(t1) if err != nil { t.Errorf("date: UnmarshalText failed (%v)", err) } if !reflect.DeepEqual(d1, d2) { t.Errorf("date: Round-trip Text failed (%v, %v)", d1, d2) } } func TestDateToTime(t *testing.T) { var d Date d, err := ParseDate("2001-02-03") if err != nil { t.Errorf("date: ParseDate failed (%v)", err) } var v interface{} = d.ToTime() switch v.(type) { case time.Time: return default: t.Errorf("date: ToTime failed to return a time.Time") } } go-autorest-0.1-alpha/autorest/date/datetime.go000066400000000000000000000037361256314551300216320ustar00rootroot00000000000000package date import ( "time" ) // Defines a type similar to time.Time but assumes a layout of RFC3339 date-time (i.e., // 2006-01-02T15:04:05Z). type DateTime struct { time.Time } // Create a new DateTime from the passed string. func ParseDateTime(date string) (d DateTime, err error) { d = DateTime{} d.Time, err = time.Parse(time.RFC3339, date) return d, err } // Preserves the DateTime as a byte array conforming to RFC3339 date-time (i.e., // 2006-01-02T15:04:05Z). func (d DateTime) MarshalBinary() ([]byte, error) { return d.Time.MarshalText() } // Reconstitutes a DateTime saved as a byte array conforming to RFC3339 date-time (i.e., // 2006-01-02T15:04:05Z). func (d *DateTime) UnmarshalBinary(data []byte) error { return d.Time.UnmarshalText(data) } // Preserves the DateTime as a JSON string conforming to RFC3339 date-time (i.e., // 2006-01-02T15:04:05Z). func (d DateTime) MarshalJSON() (json []byte, err error) { return d.Time.MarshalJSON() } // Reconstitutes the DateTime from a JSON string conforming to RFC3339 date-time (i.e., // 2006-01-02T15:04:05Z). func (d *DateTime) UnmarshalJSON(data []byte) (err error) { return d.Time.UnmarshalJSON(data) } // Preserves the DateTime as a byte array conforming to RFC3339 date-time (i.e., // 2006-01-02T15:04:05Z). func (d DateTime) MarshalText() (text []byte, err error) { return d.Time.MarshalText() } // Reconstitutes a DateTime saved as a byte array conforming to RFC3339 date-time (i.e., // 2006-01-02T15:04:05Z). func (d *DateTime) UnmarshalText(data []byte) (err error) { return d.Time.UnmarshalText(data) } // Returns the DateTime formatted as an RFC3339 date-time string (i.e., 2006-01-02T15:04:05Z). func (d DateTime) String() string { // Note: time.Time.String does not return an RFC3339 compliant string, time.Time.MarshalText does. b, err := d.Time.MarshalText() if err != nil { return "" } else { return string(b) } } // Returns a DateTime as a time.Time func (d DateTime) ToTime() time.Time { return d.Time } go-autorest-0.1-alpha/autorest/date/datetime_test.go000066400000000000000000000064031256314551300226630ustar00rootroot00000000000000package date import ( "encoding/json" "fmt" "reflect" "testing" "time" ) func ExampleParseDateTime() { d, _ := ParseDateTime("2001-02-03T04:05:06Z") fmt.Println(d) // Output: 2001-02-03T04:05:06Z } func ExampleDateTime_MarshalBinary() { d, _ := ParseDateTime("2001-02-03T04:05:06Z") t, _ := d.MarshalBinary() fmt.Println(string(t)) // Output: 2001-02-03T04:05:06Z } func ExampleDateTime_UnmarshalBinary() { d := DateTime{} t := "2001-02-03T04:05:06Z" err := d.UnmarshalBinary([]byte(t)) if err != nil { fmt.Println(err) } fmt.Println(d) // Output: 2001-02-03T04:05:06Z } func ExampleDateTime_MarshalJSON() { d, _ := ParseDateTime("2001-02-03T04:05:06Z") j, _ := json.Marshal(d) fmt.Println(string(j)) // Output: "2001-02-03T04:05:06Z" } func ExampleDateTime_UnmarshalJSON() { var d struct { DateTime DateTime `json:"datetime"` } j := `{ "datetime" : "2001-02-03T04:05:06Z" }` err := json.Unmarshal([]byte(j), &d) if err != nil { fmt.Println(err) } fmt.Println(d.DateTime) // Output: 2001-02-03T04:05:06Z } func ExampleDateTime_MarshalText() { d, _ := ParseDateTime("2001-02-03T04:05:06Z") t, _ := d.MarshalText() fmt.Println(string(t)) // Output: 2001-02-03T04:05:06Z } func ExampleDateTime_UnmarshalText() { d := DateTime{} t := "2001-02-03T04:05:06Z" err := d.UnmarshalText([]byte(t)) if err != nil { fmt.Println(err) } fmt.Println(d) // Output: 2001-02-03T04:05:06Z } func TestDateTimeString(t *testing.T) { d, _ := ParseDateTime("2001-02-03T04:05:06Z") if d.String() != "2001-02-03T04:05:06Z" { t.Errorf("date: String failed (%v)", d.String()) } } func TestDateTimeBinaryRoundTrip(t *testing.T) { d1, err := ParseDateTime("2001-02-03T04:05:06Z") t1, err := d1.MarshalBinary() if err != nil { t.Errorf("datetime: MarshalBinary failed (%v)", err) } d2 := DateTime{} err = d2.UnmarshalBinary(t1) if err != nil { t.Errorf("datetime: UnmarshalBinary failed (%v)", err) } if !reflect.DeepEqual(d1, d2) { t.Errorf("datetime: Round-trip Binary failed (%v, %v)", d1, d2) } } func TestDateTimeJSONRoundTrip(t *testing.T) { type s struct { DateTime DateTime `json:"datetime"` } var err error d1 := s{} d1.DateTime, err = ParseDateTime("2001-02-03T04:05:06Z") j, err := json.Marshal(d1) if err != nil { t.Errorf("datetime: MarshalJSON failed (%v)", err) } d2 := s{} err = json.Unmarshal(j, &d2) if err != nil { t.Errorf("datetime: UnmarshalJSON failed (%v)", err) } if !reflect.DeepEqual(d1, d2) { t.Errorf("datetime: Round-trip JSON failed (%v, %v)", d1, d2) } } func TestDateTimeTextRoundTrip(t *testing.T) { d1, err := ParseDateTime("2001-02-03T04:05:06Z") t1, err := d1.MarshalText() if err != nil { t.Errorf("datetime: MarshalText failed (%v)", err) } d2 := DateTime{} err = d2.UnmarshalText(t1) if err != nil { t.Errorf("datetime: UnmarshalText failed (%v)", err) } if !reflect.DeepEqual(d1, d2) { t.Errorf("datetime: Round-trip Text failed (%v, %v)", d1, d2) } } func TestDateTimeToTime(t *testing.T) { var d DateTime d, err := ParseDateTime("2001-02-03T04:05:06Z") if err != nil { t.Errorf("datetime: ParseDateTime failed (%v)", err) } var v interface{} = d.ToTime() switch v.(type) { case time.Time: return default: t.Errorf("datetime: ToTime failed to return a time.Time") } } go-autorest-0.1-alpha/autorest/mocks/000077500000000000000000000000001256314551300176755ustar00rootroot00000000000000go-autorest-0.1-alpha/autorest/mocks/helpers.go000066400000000000000000000020561256314551300216710ustar00rootroot00000000000000package mocks import ( "fmt" "net/http" ) func NewRequest() *http.Request { return NewRequestWithContent("") } func NewRequestWithContent(c string) *http.Request { r, _ := http.NewRequest("GET", "https://microsoft.com/a/b/c/", NewBody(c)) return r } func NewRequestForURL(u string) *http.Request { r, err := http.NewRequest("GET", u, NewBody("")) if err != nil { panic(fmt.Sprintf("mocks: ERROR (%v) parsing testing URL %s", err, u)) } return r } func NewResponse() *http.Response { return NewResponseWithContent("") } func NewResponseWithContent(c string) *http.Response { return &http.Response{ Status: "200 OK", StatusCode: 200, Proto: "HTTP/1.0", ProtoMajor: 1, ProtoMinor: 0, Body: NewBody(c), Request: NewRequest(), } } func NewResponseWithStatus(s string, c int) *http.Response { resp := NewResponse() resp.Status = s resp.StatusCode = c return resp } func AddResponseHeader(resp *http.Response, h string, v string) { if resp.Header == nil { resp.Header = make(http.Header) } resp.Header.Add(h, v) } go-autorest-0.1-alpha/autorest/mocks/mocks.go000066400000000000000000000032421256314551300213410ustar00rootroot00000000000000/* This package provides mocks and helpers used in testing. */ package mocks import ( "fmt" "io" "net/http" ) type Body struct { s string b []byte isOpen bool } func NewBody(s string) *Body { return &Body{s: s, b: []byte(s), isOpen: true} } func (body *Body) Read(b []byte) (n int, err error) { if !body.isOpen { return 0, fmt.Errorf("ERROR: Body has been closed\n") } if len(body.b) == 0 { return 0, io.EOF } n = copy(b, body.b) body.b = body.b[n:] return n, nil } func (body *Body) Close() error { body.isOpen = false return nil } func (body *Body) IsOpen() bool { return body.isOpen } type Client struct { attempts int content string emitErrors int status string statusCode int err error } func NewClient() *Client { return &Client{status: "200 OK", statusCode: 200} } func (c *Client) Do(r *http.Request) (*http.Response, error) { c.attempts += 1 resp := NewResponse() resp.Request = r resp.Body = NewBody(c.content) resp.Status = c.status resp.StatusCode = c.statusCode if c.emitErrors > 0 || c.emitErrors < 0 { c.emitErrors -= 1 if c.err == nil { return resp, fmt.Errorf("Faux Error") } else { return resp, c.err } } else { return resp, nil } } func (c *Client) Attempts() int { return c.attempts } func (c *Client) EmitErrors(emit int) { c.emitErrors = emit } func (c *Client) SetError(err error) { c.err = err } func (c *Client) ClearError() { c.SetError(nil) } func (c *Client) EmitContent(s string) { c.content = s } func (c *Client) EmitStatus(status string, code int) { c.status = status c.statusCode = code } type T struct { Name string `json:"name"` Age int `json:"age"` } go-autorest-0.1-alpha/autorest/preparer.go000066400000000000000000000243201256314551300207310ustar00rootroot00000000000000package autorest import ( "bytes" "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" "strings" ) const ( mimeTypeJSON = "application/json" mimeTypeFormPost = "application/x-www-form-urlencoded" headerAuthorization = "Authorization" headerContentType = "Content-Type" ) // Preparer is the interface that wraps the Prepare method. // // Prepare accepts and possibly modifies an http.Request (e.g., adding Headers). Implementations // must ensure to not share or hold per-invocation state since Preparers may be shared and re-used. type Preparer interface { Prepare(*http.Request) (*http.Request, error) } // PreparerFunc is a method that implements the Preparer interface. type PreparerFunc func(*http.Request) (*http.Request, error) // Prepare implements the Preparer interface on PreparerFunc. func (pf PreparerFunc) Prepare(r *http.Request) (*http.Request, error) { return pf(r) } // PrepareDecorator takes and possibly decorates, by wrapping, a Preparer. Decorators may affect the // http.Request and pass it along or, first, pass the http.Request along then affect the result. type PrepareDecorator func(Preparer) Preparer // CreatePreparer creates, decorates, and returns a Preparer. // Without decorators, the returned Preparer returns the passed http.Request unmodified. // Preparers are safe to share and re-use. func CreatePreparer(decorators ...PrepareDecorator) Preparer { return DecoratePreparer( Preparer(PreparerFunc(func(r *http.Request) (*http.Request, error) { return r, nil })), decorators...) } // DecoratePreparer accepts a Preparer and a, possibly empty, set of PrepareDecorators, which it // applies to the Preparer. Decorators are applied in the order received, but their affect upon the // request depends on whether they are a pre-decorator (change the http.Request and then pass it // along) or a post-decorator (pass the http.Request along and alter it on return). func DecoratePreparer(p Preparer, decorators ...PrepareDecorator) Preparer { for _, decorate := range decorators { p = decorate(p) } return p } // Prepare accepts an http.Request and a, possibly empty, set of PrepareDecorators. // It creates a Preparer from the decorators which it then applies to the passed http.Request. func Prepare(r *http.Request, decorators ...PrepareDecorator) (*http.Request, error) { return CreatePreparer(decorators...).Prepare(r) } // WithNothing returns a "do nothing" PrepareDecorator that makes no changes to the passed // http.Request. func WithNothing() PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { return p.Prepare(r) }) } } // WithHeader returns a PrepareDecorator that adds the specified HTTP header and value to the // http.Request. It will canonicalize the passed header name (via http.CanonicalHeaderKey) before // adding the header. func WithHeader(header string, value string) PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { if r.Header == nil { r.Header = make(http.Header) } r.Header.Add(http.CanonicalHeaderKey(header), value) } return r, err }) } } // WithBearerAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose // value is "Bearer " followed by the supplied token. func WithBearerAuthorization(token string) PrepareDecorator { return WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", token)) } // AsContentType returns a PrepareDecorator that adds an HTTP Content-Type header whose value // is the passed contentType. func AsContentType(contentType string) PrepareDecorator { return WithHeader(headerContentType, contentType) } // AsFormUrlEncoded returns a PrepareDecorator that adds an HTTP Content-Type header whose value is // "application/x-www-form-urlencoded". func AsFormUrlEncoded() PrepareDecorator { return AsContentType(mimeTypeFormPost) } // AsJSON returns a PrepareDecorator that adds an HTTP Content-Type header whose value is // "application/json". func AsJSON() PrepareDecorator { return AsContentType(mimeTypeJSON) } // WithMethod returns a PrepareDecorator that sets the HTTP method of the passed request. The // decorator does not validate that the passed method string is a known HTTP method. func WithMethod(method string) PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r.Method = method return p.Prepare(r) }) } } // AsDelete returns a PrepareDecorator that sets the HTTP method to DELETE. func AsDelete() PrepareDecorator { return WithMethod("DELETE") } // AsGet returns a PrepareDecorator that sets the HTTP method to GET. func AsGet() PrepareDecorator { return WithMethod("GET") } // AsHead returns a PrepareDecorator that sets the HTTP method to HEAD. func AsHead() PrepareDecorator { return WithMethod("HEAD") } // AsOptions returns a PrepareDecorator that sets the HTTP method to OPTIONS. func AsOptions() PrepareDecorator { return WithMethod("OPTIONS") } // AsPatch returns a PrepareDecorator that sets the HTTP method to PATCH. func AsPatch() PrepareDecorator { return WithMethod("PATCH") } // AsPost returns a PrepareDecorator that sets the HTTP method to POST. func AsPost() PrepareDecorator { return WithMethod("POST") } // AsPut returns a PrepareDecorator that sets the HTTP method to PUT. func AsPut() PrepareDecorator { return WithMethod("PUT") } // WithURL returns a PrepareDecorator that populates the http.Request with a url.URL constructed // from the supplied baseUrl. func WithBaseURL(baseUrl string) PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { u, err := url.Parse(baseUrl) if err == nil { r.URL = u } } return r, err }) } } // WithFormData returns a PrepareDecoratore that "URL encodes" (e.g., bar=baz&foo=quux) into the // http.Request body. func WithFormData(v url.Values) PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { s := v.Encode() r.ContentLength = int64(len(s)) r.Body = ioutil.NopCloser(strings.NewReader(s)) } return r, err }) } } // WithJSON returns a PrepareDecorator that encodes the data passed as JSON into the body of the // request and sets the Content-Length header. func WithJSON(v interface{}) PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { b, err := json.Marshal(v) if err == nil { r.ContentLength = int64(len(b)) r.Body = ioutil.NopCloser(bytes.NewReader(b)) } } return r, err }) } } // WithPath returns a PrepareDecorator that adds the supplied path to the request URL. If the path // is absolute (that is, it begins with a "/"), it replaces the existing path. func WithPath(path string) PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { if r.URL == nil { return r, fmt.Errorf("autorest: WithPath invoked with a nil URL") } u := r.URL u.Path = strings.TrimRight(u.Path, "/") if strings.HasPrefix(path, "/") { u.Path = path } else { u.Path += "/" + path } } return r, err }) } } // WithEscapedPathParameters returns a PrepareDecorator that replaces brace-enclosed keys within the // request path (i.e., http.Request.URL.Path) with the corresponding values from the passed map. The // values will be escaped (aka URL encoded) before insertion into the path. func WithEscapedPathParameters(pathParameters map[string]interface{}) PrepareDecorator { parameters := escapeValueStrings(ensureValueStrings(pathParameters)) return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { if r.URL == nil { return r, fmt.Errorf("autorest: WithEscapedPathParameters invoked with a nil URL") } for key, value := range parameters { r.URL.Path = strings.Replace(r.URL.Path, "{"+key+"}", value, -1) } } return r, err }) } } // WithPathParameters returns a PrepareDecorator that replaces brace-enclosed keys within the // request path (i.e., http.Request.URL.Path) with the corresponding values from the passed map. func WithPathParameters(pathParameters map[string]interface{}) PrepareDecorator { parameters := ensureValueStrings(pathParameters) return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { if r.URL == nil { return r, fmt.Errorf("autorest: WithPathParameters invoked with a nil URL") } for key, value := range parameters { r.URL.Path = strings.Replace(r.URL.Path, "{"+key+"}", value, -1) } } return r, err }) } } // WithQueryParameters returns a PrepareDecorators that encodes and applies the query parameters // given in the supplied map (i.e., key=value). func WithQueryParameters(queryParameters map[string]interface{}) PrepareDecorator { parameters := ensureValueStrings(queryParameters) return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { if r.URL == nil { return r, fmt.Errorf("autorest: WithQueryParameters invoked with a nil URL") } v := r.URL.Query() for key, value := range parameters { v.Add(key, value) } r.URL.RawQuery = v.Encode() } return r, err }) } } // Authorizer is the interface that provides a PrepareDecorator used to supply request // authorization. Most often, the Authorizer decorator runs last so it has access to the full // state of the formed HTTP request. type Authorizer interface { WithAuthorization() PrepareDecorator } // NullAuthorizer implements a default, "do nothing" Authorizer. type NullAuthorizer struct{} // WithAuthorization returns a PrepareDecorator that does nothing. func (na NullAuthorizer) WithAuthorization() PrepareDecorator { return WithNothing() } go-autorest-0.1-alpha/autorest/preparer_test.go000066400000000000000000000275241256314551300220010ustar00rootroot00000000000000package autorest import ( "fmt" "io/ioutil" "net/http" "net/url" "reflect" "testing" "github.com/azure/go-autorest/autorest/mocks" ) // PrepareDecorators wrap and invoke a Preparer. Most often, the decorator invokes the passed // Preparer and decorates the response. func ExamplePrepareDecorator(path string) PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { if r.URL == nil { return r, fmt.Errorf("ERROR: URL is not set") } r.URL.Path += path } return r, err }) } } // PrepareDecorators may also modify and then invoke the Preparer. func ExamplePrepareDecorator_pre(path string) PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r.Header.Add(http.CanonicalHeaderKey("ContentType"), "application/json") return p.Prepare(r) }) } } // Create a sequence of three Preparers that build up the URL path. func ExampleCreatePreparer() { p := CreatePreparer( WithBaseURL("https://microsoft.com/"), WithPath("a"), WithPath("b"), WithPath("c")) r, err := p.Prepare(&http.Request{}) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Println(r.URL) } // Output: https://microsoft.com/a/b/c } // Create and apply separate Preparers func ExampleCreatePreparer_multiple() { params := map[string]interface{}{ "param1": "a", "param2": "c", } p1 := CreatePreparer(WithBaseURL("https://microsoft.com/"), WithPath("/{param1}/b/{param2}/")) p2 := CreatePreparer(WithPathParameters(params)) r, err := p1.Prepare(&http.Request{}) if err != nil { fmt.Printf("ERROR: %v\n", err) } r, err = p2.Prepare(r) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Println(r.URL) } // Output: https://microsoft.com/a/b/c/ } // Create and chain separate Preparers func ExampleCreatePreparer_chain() { params := map[string]interface{}{ "param1": "a", "param2": "c", } p := CreatePreparer(WithBaseURL("https://microsoft.com/"), WithPath("/{param1}/b/{param2}/")) p = DecoratePreparer(p, WithPathParameters(params)) r, err := p.Prepare(&http.Request{}) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Println(r.URL) } // Output: https://microsoft.com/a/b/c/ } // Create and prepare an http.Request in one call func ExamplePrepare() { r, err := Prepare(&http.Request{}, AsGet(), WithBaseURL("https://microsoft.com/"), WithPath("a/b/c/")) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Printf("%s %s", r.Method, r.URL) } // Output: GET https://microsoft.com/a/b/c/ } // Create a request for a supplied base URL and path func ExampleWithBaseURL() { r, err := Prepare(&http.Request{}, WithBaseURL("https://microsoft.com/a/b/c/")) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Println(r.URL) } // Output: https://microsoft.com/a/b/c/ } // Create a request with a custom HTTP header func ExampleWithHeader() { r, err := Prepare(&http.Request{}, WithBaseURL("https://microsoft.com/a/b/c/"), WithHeader("x-foo", "bar")) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Printf("Header %s=%s\n", "x-foo", r.Header.Get("x-foo")) } // Output: Header x-foo=bar } // Create a request whose Body is the JSON encoding of a structure func ExampleWithFormData() { v := url.Values{} v.Add("name", "Rob Pike") v.Add("age", "42") r, err := Prepare(&http.Request{}, WithFormData(v)) if err != nil { fmt.Printf("ERROR: %v\n", err) } b, err := ioutil.ReadAll(r.Body) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Printf("Request Body contains %s\n", string(b)) } // Output: Request Body contains age=42&name=Rob+Pike } // Create a request whose Body is the JSON encoding of a structure func ExampleWithJSON() { t := mocks.T{Name: "Rob Pike", Age: 42} r, err := Prepare(&http.Request{}, WithJSON(&t)) if err != nil { fmt.Printf("ERROR: %v\n", err) } b, err := ioutil.ReadAll(r.Body) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Printf("Request Body contains %s\n", string(b)) } // Output: Request Body contains {"name":"Rob Pike","age":42} } // Create a request from a path with parameters func ExampleWithPathParameters() { params := map[string]interface{}{ "param1": "a", "param2": "c", } r, err := Prepare(&http.Request{}, WithBaseURL("https://microsoft.com/"), WithPath("/{param1}/b/{param2}/"), WithPathParameters(params)) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Println(r.URL) } // Output: https://microsoft.com/a/b/c/ } // Create a request with query parameters func ExampleWithQueryParameters() { params := map[string]interface{}{ "q1": "value1", "q2": "value2", } r, err := Prepare(&http.Request{}, WithBaseURL("https://microsoft.com/"), WithPath("/a/b/c/"), WithQueryParameters(params)) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Println(r.URL) } // Output: https://microsoft.com/a/b/c/?q1=value1&q2=value2 } func TestCreatePreparerDoesNotModify(t *testing.T) { r1 := &http.Request{} p := CreatePreparer() r2, err := p.Prepare(r1) if err != nil { t.Errorf("autorest: CreatePreparer failed (%v)", err) } if !reflect.DeepEqual(r1, r2) { t.Errorf("autorest: CreatePreparer without decorators modified the request") } } func TestCreatePreparerRunsDecoratorsInOrder(t *testing.T) { p := CreatePreparer(WithBaseURL("https://microsoft.com/"), WithPath("1"), WithPath("2"), WithPath("3")) r, err := p.Prepare(&http.Request{}) if err != nil { t.Errorf("autorest: CreatePreparer failed (%v)", err) } if r.URL.String() != "https://microsoft.com/1/2/3" { t.Errorf("autorest: CreatePreparer failed to run decorators in order") } } func TestAsContentType(t *testing.T) { r, err := Prepare(mocks.NewRequest(), AsContentType("application/text")) if err != nil { fmt.Printf("ERROR: %v", err) } if r.Header.Get(headerContentType) != "application/text" { t.Errorf("autorest: AsContentType failed to add header (%s=%s)", headerContentType, r.Header.Get(headerContentType)) } } func TestAsFormUrlEncoded(t *testing.T) { r, err := Prepare(mocks.NewRequest(), AsFormUrlEncoded()) if err != nil { fmt.Printf("ERROR: %v", err) } if r.Header.Get(headerContentType) != mimeTypeFormPost { t.Errorf("autorest: AsFormUrlEncoded failed to add header (%s=%s)", headerContentType, r.Header.Get(headerContentType)) } } func TestAsJSON(t *testing.T) { r, err := Prepare(mocks.NewRequest(), AsJSON()) if err != nil { fmt.Printf("ERROR: %v", err) } if r.Header.Get(headerContentType) != mimeTypeJSON { t.Errorf("autorest: AsJSON failed to add header (%s=%s)", headerContentType, r.Header.Get(headerContentType)) } } func TestWithNothing(t *testing.T) { r1 := mocks.NewRequest() r2, err := Prepare(r1, WithNothing()) if err != nil { t.Errorf("autorest: WithNothing returned an unexpected error (%v)", err) } if !reflect.DeepEqual(r1, r2) { t.Error("azure: WithNothing modified the passed HTTP Request") } } func TestWithBearerAuthorization(t *testing.T) { r, err := Prepare(mocks.NewRequest(), WithBearerAuthorization("SOME-TOKEN")) if err != nil { fmt.Printf("ERROR: %v", err) } if r.Header.Get(headerAuthorization) != "Bearer SOME-TOKEN" { t.Errorf("autorest: WithBearerAuthorization failed to add header (%s=%s)", headerAuthorization, r.Header.Get(headerAuthorization)) } } func TestWithMethod(t *testing.T) { r, _ := Prepare(mocks.NewRequest(), WithMethod("HEAD")) if r.Method != "HEAD" { t.Error("autorest: WithMethod failed to set HTTP method header") } } func TestAsDelete(t *testing.T) { r, _ := Prepare(mocks.NewRequest(), AsDelete()) if r.Method != "DELETE" { t.Error("autorest: AsDelete failed to set HTTP method header to DELETE") } } func TestAsGet(t *testing.T) { r, _ := Prepare(mocks.NewRequest(), AsGet()) if r.Method != "GET" { t.Error("autorest: AsGet failed to set HTTP method header to GET") } } func TestAsHead(t *testing.T) { r, _ := Prepare(mocks.NewRequest(), AsHead()) if r.Method != "HEAD" { t.Error("autorest: AsHead failed to set HTTP method header to HEAD") } } func TestAsOptions(t *testing.T) { r, _ := Prepare(mocks.NewRequest(), AsOptions()) if r.Method != "OPTIONS" { t.Error("autorest: AsOptions failed to set HTTP method header to OPTIONS") } } func TestAsPatch(t *testing.T) { r, _ := Prepare(mocks.NewRequest(), AsPatch()) if r.Method != "PATCH" { t.Error("autorest: AsPatch failed to set HTTP method header to PATCH") } } func TestAsPost(t *testing.T) { r, _ := Prepare(mocks.NewRequest(), AsPost()) if r.Method != "POST" { t.Error("autorest: AsPost failed to set HTTP method header to POST") } } func TestAsPut(t *testing.T) { r, _ := Prepare(mocks.NewRequest(), AsPut()) if r.Method != "PUT" { t.Error("autorest: AsPut failed to set HTTP method header to PUT") } } func TestWithFormDataSetsContentLength(t *testing.T) { v := url.Values{} v.Add("name", "Rob Pike") v.Add("age", "42") r, err := Prepare(&http.Request{}, WithFormData(v)) if err != nil { t.Errorf("autorest: WithFormData failed with error (%v)", err) } b, err := ioutil.ReadAll(r.Body) if err != nil { t.Errorf("autorest: WithFormData failed with error (%v)", err) } if r.ContentLength != int64(len(b)) { t.Errorf("autorest:WithFormData set Content-Length to %v, expected %v", r.ContentLength, len(b)) } } func TestWithJSONSetsContentLength(t *testing.T) { r, err := Prepare(&http.Request{}, WithJSON(&mocks.T{Name: "Rob Pike", Age: 42})) if err != nil { t.Errorf("autorest: WithJSON failed with error (%v)", err) } b, err := ioutil.ReadAll(r.Body) if err != nil { t.Errorf("autorest: WithJSON failed with error (%v)", err) } if r.ContentLength != int64(len(b)) { t.Errorf("autorest:WithJSON set Content-Length to %v, expected %v", r.ContentLength, len(b)) } } func TestWithHeaderAllocatesHeaders(t *testing.T) { r, err := Prepare(mocks.NewRequest(), WithHeader("x-foo", "bar")) if err != nil { t.Errorf("autorest: WithHeader failed (%v)", err) } if r.Header.Get("x-foo") != "bar" { t.Errorf("autorest: WithHeader failed to add header (%s=%s)", "x-foo", r.Header.Get("x-foo")) } } func TestWithPathCatchesNilURL(t *testing.T) { _, err := Prepare(&http.Request{}, WithPath("a")) if err == nil { t.Errorf("autorest: WithPath failed to catch a nil URL") } } func TestWithEscapedPathParametersCatchesNilURL(t *testing.T) { _, err := Prepare(&http.Request{}, WithEscapedPathParameters(map[string]interface{}{"foo": "bar"})) if err == nil { t.Errorf("autorest: WithEscapedPathParameters failed to catch a nil URL") } } func TestWithPathParametersCatchesNilURL(t *testing.T) { _, err := Prepare(&http.Request{}, WithPathParameters(map[string]interface{}{"foo": "bar"})) if err == nil { t.Errorf("autorest: WithPathParameters failed to catch a nil URL") } } func TestWithQueryParametersCatchesNilURL(t *testing.T) { _, err := Prepare(&http.Request{}, WithQueryParameters(map[string]interface{}{"foo": "bar"})) if err == nil { t.Errorf("autorest: WithQueryParameters failed to catch a nil URL") } } func TestModifyingExistingRequest(t *testing.T) { r, err := Prepare(mocks.NewRequestForURL("https://bing.com"), WithPath("search"), WithQueryParameters(map[string]interface{}{"q": "golang"})) if err != nil { t.Errorf("autorest: Preparing an existing request returned an error (%v)", err) } if r.URL.String() != "https://bing.com/search?q=golang" { t.Errorf("autorest: Preparing an existing request failed (%s)", r.URL) } } func TestWithAuthorizer(t *testing.T) { r1 := mocks.NewRequest() na := &NullAuthorizer{} r2, err := Prepare(r1, na.WithAuthorization()) if err != nil { t.Errorf("autorest: NullAuthorizer#WithAuthorization returned an unexpected error (%v)", err) } else if !reflect.DeepEqual(r1, r2) { t.Errorf("autorest: NullAuthorizer#WithAuthorization modified the request -- received %v, expected %v", r2, r1) } } go-autorest-0.1-alpha/autorest/responder.go000066400000000000000000000115511256314551300211140ustar00rootroot00000000000000package autorest import ( "encoding/json" "fmt" "net/http" ) // Responder is the interface that wraps the Respond method. // // Respond accepts and reacts to an http.Response. Implementations must ensure to not share or hold // state since Responders may be shared and re-used. type Responder interface { Respond(*http.Response) error } // ResponderFunc is a method that implements the Responder interface. type ResponderFunc func(*http.Response) error // Respond implements the Responder interface on ResponderFunc. func (rf ResponderFunc) Respond(r *http.Response) error { return rf(r) } // RespondDecorator takes and possibly decorates, by wrapping, a Responder. Decorators may react to // the http.Response and pass it along or, first, pass the http.Response along then react. type RespondDecorator func(Responder) Responder // CreateResponder creates, decorates, and returns a Responder. Without decorators, the returned // Responder returns the passed http.Response unmodified. Responders may or may not be safe to share // and re-used: It depends on the applied decorators. For example, a standard decorator that closes // the response body is fine to share whereas a decorator that reads the body into a passed struct // is not. // // To prevent memory leaks, ensure that at least one Responder closes the response body. func CreateResponder(decorators ...RespondDecorator) Responder { return DecorateResponder( Responder(ResponderFunc(func(r *http.Response) error { return nil })), decorators...) } // DecorateResponder accepts a Responder and a, possibly empty, set of RespondDecorators, which it // applies to the Responder. Decorators are applied in the order received, but their affect upon the // request depends on whether they are a pre-decorator (react to the http.Response and then pass it // along) or a post-decorator (pass the http.Response along and then react). func DecorateResponder(r Responder, decorators ...RespondDecorator) Responder { for _, decorate := range decorators { r = decorate(r) } return r } // Respond accepts an http.Response and a, possibly empty, set of RespondDecorators. // It creates a Responder from the decorators it then applies to the passed http.Response. func Respond(r *http.Response, decorators ...RespondDecorator) error { return CreateResponder(decorators...).Respond(r) } // ByIgnoring returns a RespondDecorator that ignores the passed http.Response by passing it // unexamined to the next RespondDecorator. func ByIgnoring() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { return r.Respond(resp) }) } } // ByClosing returns a RespondDecorator that first invokes the passed Responder after which it // closes the response body. Since the passed Responder is invoked prior to closing the response // body, the decorator may occur anywhere within the set. func ByClosing() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { err := r.Respond(resp) if resp != nil && resp.Body != nil { resp.Body.Close() } return err }) } } // ByClosingIfError returns a RespondDecorator that first invokes the passed Responder after which // it closes the response if the passed Responder returns an error and the response body exists. // This decorator should, usually, come first in the set. func ByClosingIfError() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { err := r.Respond(resp) if err != nil && resp != nil && resp.Body != nil { resp.Body.Close() } return err }) } } // ByUnmarshallingJson returns a RespondDecorator that decodes a JSON document returned in the // response Body into the value pointed to by v. func ByUnmarshallingJSON(v interface{}) RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { err := r.Respond(resp) if err == nil { d := json.NewDecoder(resp.Body) err = d.Decode(v) } return err }) } } // WithErrorUnlessStatusCode returns a RespondDecorator that emits an error unless the response // StatusCode is among the set passed. Since these are artificial errors, the response body // may still require closing. func WithErrorUnlessStatusCode(codes ...int) RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { err := r.Respond(resp) if err == nil && !ResponseHasStatusCode(resp, codes...) { err = fmt.Errorf("autorest: %v request to %v failed with StatusCode %s", resp.Request.Method, resp.Request.URL, resp.Status) } return err }) } } // WithErrorUnlessOK returns a RespondDecorator that emits an error if the response StatusCode is // anything other than HTTP 200. func WithErrorUnlessOK() RespondDecorator { return WithErrorUnlessStatusCode(200) } go-autorest-0.1-alpha/autorest/responder_test.go000066400000000000000000000163511256314551300221560ustar00rootroot00000000000000package autorest import ( "fmt" "net/http" "reflect" "testing" "github.com/azure/go-autorest/autorest/mocks" ) const ( jsonT = ` { "name":"Rob Pike", "age":42 }` ) func ExampleWithErrorUnlessOK() { r := mocks.NewResponse() r.Request = mocks.NewRequest() // Respond and leave the response body open (for a subsequent responder to close) err := Respond(r, WithErrorUnlessOK(), ByClosingIfError()) if err == nil { fmt.Printf("%s of %s returned HTTP 200", r.Request.Method, r.Request.URL) // Complete handling the response and close the body Respond(r, ByClosing()) } // Output: GET of https://microsoft.com/a/b/c/ returned HTTP 200 } func ExampleByUnmarshallingJSON() { c := ` { "name" : "Rob Pike", "age" : 42 } ` type V struct { Name string `json:"name"` Age int `json:"age"` } v := &V{} Respond(mocks.NewResponseWithContent(c), ByUnmarshallingJSON(v), ByClosing()) fmt.Printf("%s is %d years old\n", v.Name, v.Age) // Output: Rob Pike is 42 years old } func TestCreateResponderDoesNotModify(t *testing.T) { r1 := mocks.NewResponse() r2 := mocks.NewResponse() p := CreateResponder() err := p.Respond(r1) if err != nil { t.Errorf("autorest: CreateResponder failed (%v)", err) } if !reflect.DeepEqual(r1, r2) { t.Errorf("autorest: CreateResponder without decorators modified the response") } } func TestCreateResponderRunsDecoratorsInOrder(t *testing.T) { s := "" d := func(n int) RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { err := r.Respond(resp) if err == nil { s += fmt.Sprintf("%d", n) } return err }) } } p := CreateResponder(d(1), d(2), d(3)) err := p.Respond(&http.Response{}) if err != nil { t.Errorf("autorest: Respond failed (%v)", err) } if s != "123" { t.Errorf("autorest: CreateResponder invoked decorators in an incorrect order; expected '123', received '%s'", s) } } func TestByIgnoring(t *testing.T) { r := mocks.NewResponse() Respond(r, (func() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(r2 *http.Response) error { r1 := mocks.NewResponse() if !reflect.DeepEqual(r1, r2) { t.Errorf("autorest: ByIgnoring modified the HTTP Response -- received %v, expected %v", r2, r1) } return nil }) } })(), ByIgnoring(), ByClosing()) } func TestByClosing(t *testing.T) { r := mocks.NewResponse() err := Respond(r, ByClosing()) if err != nil { t.Errorf("autorest: ByClosing failed (%v)", err) } if r.Body.(*mocks.Body).IsOpen() { t.Errorf("autorest: ByClosing did not close the response body") } } func TestByClosingAcceptsNilResponse(t *testing.T) { r := mocks.NewResponse() Respond(r, (func() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { resp.Body.Close() r.Respond(nil) return nil }) } })(), ByClosing()) } func TestByClosingAcceptsNilBody(t *testing.T) { r := mocks.NewResponse() Respond(r, (func() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { resp.Body.Close() resp.Body = nil r.Respond(resp) return nil }) } })(), ByClosing()) } func TestByClosingClosesEvenAfterErrors(t *testing.T) { var e error r := mocks.NewResponse() Respond(r, withErrorRespondDecorator(&e), ByClosing()) if r.Body.(*mocks.Body).IsOpen() { t.Errorf("autorest: ByClosing did not close the response body after an error occurred") } } func TestByClosingClosesReturnsNestedErrors(t *testing.T) { var e error r := mocks.NewResponse() err := Respond(r, withErrorRespondDecorator(&e), ByClosing()) if err == nil || !reflect.DeepEqual(e, err) { t.Errorf("autorest: ByClosing failed to return a nested error") } } func TestByClosingIfErrorAcceptsNilResponse(t *testing.T) { var e error r := mocks.NewResponse() Respond(r, withErrorRespondDecorator(&e), (func() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { resp.Body.Close() r.Respond(nil) return nil }) } })(), ByClosingIfError()) } func TestByClosingIfErrorAcceptsNilBody(t *testing.T) { var e error r := mocks.NewResponse() Respond(r, withErrorRespondDecorator(&e), (func() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { resp.Body.Close() resp.Body = nil r.Respond(resp) return nil }) } })(), ByClosingIfError()) } func TestByClosingIfErrorClosesIfAnErrorOccurs(t *testing.T) { var e error r := mocks.NewResponse() Respond(r, withErrorRespondDecorator(&e), ByClosingIfError()) if r.Body.(*mocks.Body).IsOpen() { t.Errorf("autorest: ByClosingIfError did not close the response body after an error occurred") } } func TestByClosingIfErrorDoesNotClosesIfNoErrorOccurs(t *testing.T) { r := mocks.NewResponse() Respond(r, ByClosingIfError()) if !r.Body.(*mocks.Body).IsOpen() { t.Errorf("autorest: ByClosingIfError closed the response body even though no error occurred") } } func TestByUnmarhallingJSON(t *testing.T) { v := &mocks.T{} r := mocks.NewResponseWithContent(jsonT) err := Respond(r, ByUnmarshallingJSON(v), ByClosing()) if err != nil { t.Errorf("autorest: ByUnmarshallingJSON failed (%v)", err) } if v.Name != "Rob Pike" || v.Age != 42 { t.Errorf("autorest: ByUnmarshallingJSON failed to properly unmarshal") } } func TestWithErrorUnlessStatusCode(t *testing.T) { r := mocks.NewResponse() r.Request = mocks.NewRequest() r.Status = "400 BadRequest" r.StatusCode = 400 err := Respond(r, WithErrorUnlessStatusCode(300, 400, 500), ByClosingIfError()) if err != nil { t.Errorf("autorest: WithErrorUnlessStatusCode returned an error (%v) for an acceptable status code (%s)", err, r.Status) } } func TestWithErrorUnlessStatusCodeEmitsErrorForUnacceptableStatusCode(t *testing.T) { r := mocks.NewResponse() r.Request = mocks.NewRequest() r.Status = "400 BadRequest" r.StatusCode = 400 err := Respond(r, WithErrorUnlessStatusCode(200, 300, 500), ByClosingIfError()) if err == nil { t.Errorf("autorest: WithErrorUnlessStatusCode failed to return an error for an unacceptable status code (%s)", r.Status) } } func TestWithErrorUnlessOK(t *testing.T) { r := mocks.NewResponse() r.Request = mocks.NewRequest() err := Respond(r, WithErrorUnlessOK(), ByClosingIfError()) if err != nil { t.Errorf("autorest: WithErrorUnlessOK returned an error for OK status code (%v)", err) } } func TestWithErrorUnlessOKEmitsErrorIfNotOK(t *testing.T) { r := mocks.NewResponse() r.Request = mocks.NewRequest() r.Status = "400 BadRequest" r.StatusCode = 400 err := Respond(r, WithErrorUnlessOK(), ByClosingIfError()) if err == nil { t.Errorf("autorest: WithErrorUnlessOK failed to return an error for a non-OK status code (%v)", err) } } func withErrorRespondDecorator(e *error) RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { err := r.Respond(resp) if err != nil { return err } *e = fmt.Errorf("autorest: Faux Error") return *e }) } } go-autorest-0.1-alpha/autorest/sender.go000066400000000000000000000154361256314551300204010ustar00rootroot00000000000000package autorest import ( "fmt" "log" "math" "net/http" "time" ) // Sender is the interface that wraps the Do method to send HTTP requests. // // The standard http.Client conforms to this interface. type Sender interface { Do(*http.Request) (*http.Response, error) } // SenderFunc is a method that implements the Sender interface. type SenderFunc func(*http.Request) (*http.Response, error) // Do implements the Sender interface on SenderFunc. func (sf SenderFunc) Do(r *http.Request) (*http.Response, error) { return sf(r) } // SendDecorator takes and possibily decorates, by wrapping, a Sender. Decorators may affect the // http.Request and pass it along or, first, pass the http.Request along then react to the // http.Response result. type SendDecorator func(Sender) Sender // CreateSender creates, decorates, and returns, as a Sender, the default http.Client. func CreateSender(decorators ...SendDecorator) Sender { return DecorateSender(&http.Client{}, decorators...) } // DecorateSender accepts a Sender and a, possibly empty, set of SendDecorators, which is applies to // the Sender. Decorators are applied in the order received, but their affect upon the request // depends on whether they are a pre-decorator (change the http.Request and then pass it along) or a // post-decorator (pass the http.Request along and react to the results in http.Response). func DecorateSender(s Sender, decorators ...SendDecorator) Sender { for _, decorate := range decorators { s = decorate(s) } return s } // Send sends, by means of the default http.Client, the passed http.Request, returning the // http.Response and possible error. It also accepts a, possibly empty, set of SendDecorators which // it will apply the http.Client before invoking the Do method. // // Send is a convenience method and not recommended for production. Advanced users should use // SendWithSender, passing and sharing their own Sender (e.g., instance of http.Client). // // Send will not poll or retry requests. func Send(r *http.Request, decorators ...SendDecorator) (*http.Response, error) { return SendWithSender(&http.Client{}, r, decorators...) } // SendWithSender sends the passed http.Request, through the provided Sender, returning the // http.Response and possible error. It also accepts a, possibly empty, set of SendDecorators which // it will apply the http.Client before invoking the Do method. // // SendWithSender will not poll or retry requests. func SendWithSender(s Sender, r *http.Request, decorators ...SendDecorator) (*http.Response, error) { return DecorateSender(s, decorators...).Do(r) } // AfterDelay returns a SendDecorator that delays for the passed time.Duration before // invoking the Sender. func AfterDelay(d time.Duration) SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { time.Sleep(d) return s.Do(r) }) } } // AsIs returns a SendDecorator that invokes the passed Sender without modifying the http.Request. func AsIs() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { return s.Do(r) }) } } // WithLogging returns a SendDecorator that implements simple before and after logging of the // request. func WithLogging(scope string, logger *log.Logger) SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { logger.Printf("%s: Sending %s %s\n", scope, r.Method, r.URL) resp, err := s.Do(r) logger.Printf("%s: %s %s received %s\n", scope, r.Method, r.URL, resp.Status) return resp, err }) } } // DoCloseIfError returns a SendDecorator that first invokes the passed Sender after which // it closes the response if the passed Sender returns an error and the response body exists. func DoCloseIfError() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { resp, err := s.Do(r) if err != nil { Respond(resp, ByClosing()) } return resp, err }) } } // DoErrorIfStatusCode returns a SendDecorator that emits an error if the response StatusCode is // among the set passed. Since these are artificial errors, the response body may still require // closing. func DoErrorIfStatusCode(codes ...int) SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { resp, err := s.Do(r) if err == nil && ResponseHasStatusCode(resp, codes...) { err = fmt.Errorf("autorest: %v request to %v failed with StatusCode %s", resp.Request.Method, resp.Request.URL, resp.Status) } return resp, err }) } } // DoErrorUnlessStatusCode returns a SendDecorator that emits an error unless the response // StatusCode is among the set passed. Since these are artificial errors, the response body // may still require closing. func DoErrorUnlessStatusCode(codes ...int) SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { resp, err := s.Do(r) if err == nil && !ResponseHasStatusCode(resp, codes...) { err = fmt.Errorf("autorest: %v request to %v failed with StatusCode %s", resp.Request.Method, resp.Request.URL, resp.Status) } return resp, err }) } } // DoRetryOnStatusCodesForAttempts returns a SendDecorator that retries the request for up to the // specified number of attempts, exponentially backing off between requests using the supplied // backoff time.Duration (which may be zero). func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (resp *http.Response, err error) { for attempt := 0; attempt < attempts; attempt++ { resp, err = s.Do(r) if err == nil { return resp, err } DelayForBackoff(backoff, attempt) } return resp, err }) } } // DoRetryForDuration returns a SendDecorator that retries the request until the total time is equal // to or greater than the specified duration, exponentially backing off between requests using the // supplied backoff time.Duration (which may be zero). func DoRetryForDuration(d time.Duration, backoff time.Duration) SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (resp *http.Response, err error) { end := time.Now().Add(d) for attempt := 0; time.Now().Before(end); attempt++ { resp, err = s.Do(r) if err == nil { return resp, err } DelayForBackoff(backoff, attempt) } return resp, err }) } } // DelayForBackoff invokes time.Sleep for the supplied backoff duration raised to the power of // passed attempt (i.e., an exponential backoff delay). Backoff may be zero. func DelayForBackoff(backoff time.Duration, attempt int) { time.Sleep(time.Duration(math.Pow(float64(backoff), float64(attempt)))) } go-autorest-0.1-alpha/autorest/sender_test.go000066400000000000000000000255701256314551300214400ustar00rootroot00000000000000package autorest import ( "fmt" "log" "net/http" "os" "reflect" "testing" "time" "github.com/azure/go-autorest/autorest/mocks" ) func ExampleSendWithSender() { client := mocks.NewClient() client.EmitStatus("202 Accepted", 202) logger := log.New(os.Stdout, "", 0) na := NullAuthorizer{} req, _ := Prepare(&http.Request{}, AsGet(), WithBaseURL("https://microsoft.com/a/b/c/"), na.WithAuthorization()) r, _ := SendWithSender(client, req, WithLogging("autorest", logger), DoErrorIfStatusCode(202), DoCloseIfError(), DoRetryForAttempts(5, time.Duration(0))) Respond(r, ByClosing()) // Output: // autorest: Sending GET https://microsoft.com/a/b/c/ // autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted // autorest: Sending GET https://microsoft.com/a/b/c/ // autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted // autorest: Sending GET https://microsoft.com/a/b/c/ // autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted // autorest: Sending GET https://microsoft.com/a/b/c/ // autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted // autorest: Sending GET https://microsoft.com/a/b/c/ // autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted } func ExampleDoRetryForAttempts() { client := mocks.NewClient() client.EmitErrors(10) // Retry with backoff -- ensure returned Bodies are closed r, _ := SendWithSender(client, mocks.NewRequest(), DoCloseIfError(), DoRetryForAttempts(5, time.Duration(0))) Respond(r, ByClosing()) fmt.Printf("Retry stopped after %d attempts", client.Attempts()) // Output: Retry stopped after 5 attempts } func ExampleDoErrorIfStatusCode() { client := mocks.NewClient() client.EmitStatus("204 NoContent", 204) // Chain decorators to retry the request, up to five times, if the status code is 204 r, _ := SendWithSender(client, mocks.NewRequest(), DoErrorIfStatusCode(204), DoCloseIfError(), DoRetryForAttempts(5, time.Duration(0))) Respond(r, ByClosing()) fmt.Printf("Retry stopped after %d attempts with code %s", client.Attempts(), r.Status) // Output: Retry stopped after 5 attempts with code 204 NoContent } func TestSendWithSenderRunsDecoratorsInOrder(t *testing.T) { client := mocks.NewClient() s := "" r, err := SendWithSender(client, mocks.NewRequest(), withMessage(&s, "a"), withMessage(&s, "b"), withMessage(&s, "c")) if err != nil { t.Errorf("autorest: SendWithSender returned an error (%v)", err) } Respond(r, ByClosing()) if s != "abc" { t.Errorf("autorest: SendWithSender invoke decorators out of order; expected 'abc', received '%s'", s) } } func TestAfterDelayWaits(t *testing.T) { client := mocks.NewClient() // Establish a baseline and then set the wait to 10x that amount // -- Waiting 10x the baseline should be long enough for a real test while not slowing the // tests down too much tt := time.Now() SendWithSender(client, mocks.NewRequest()) d := 10 * time.Since(tt) tt = time.Now() r, _ := SendWithSender(client, mocks.NewRequest(), AfterDelay(d)) s := time.Since(tt) if s < d { t.Error("autorest: AfterDelay failed to wait for at least the specified duration") } Respond(r, ByClosing()) } func TestAfterDelayDoesNotWaitTooLong(t *testing.T) { client := mocks.NewClient() // Establish a baseline and then set the wait to 10x that amount // -- Waiting 10x the baseline should be long enough for a real test while not slowing the // tests down too much tt := time.Now() SendWithSender(client, mocks.NewRequest()) d := 10 * time.Since(tt) tt = time.Now() r, _ := SendWithSender(client, mocks.NewRequest(), AfterDelay(d)) s := time.Since(tt) if s > 5*d { t.Error("autorest: AfterDelay waited too long (more than five times the specified duration") } Respond(r, ByClosing()) } func TestAsIs(t *testing.T) { client := mocks.NewClient() r1 := mocks.NewResponse() r2, err := SendWithSender(client, mocks.NewRequest(), AsIs(), (func() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { return r1, nil }) } })()) if err != nil { t.Errorf("autorest: AsIs returned an unexpected error (%v)", err) } else if !reflect.DeepEqual(r1, r2) { t.Errorf("autorest: AsIs modified the response -- received %v, expected %v", r2, r1) } Respond(r1, ByClosing()) Respond(r2, ByClosing()) } func TestDoCloseIfError(t *testing.T) { client := mocks.NewClient() client.EmitStatus("400 BadRequest", 400) r, _ := SendWithSender(client, mocks.NewRequest(), DoErrorIfStatusCode(400), DoCloseIfError()) if r.Body.(*mocks.Body).IsOpen() { t.Error("autorest: Expected DoCloseIfError to close response body -- it was left open") } Respond(r, ByClosing()) } func TestDoCloseIfErrorAcceptsNilResponse(t *testing.T) { client := mocks.NewClient() SendWithSender(client, mocks.NewRequest(), (func() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { resp, err := s.Do(r) if err != nil { resp.Body.Close() } return nil, fmt.Errorf("Faux Error") }) } })(), DoCloseIfError()) } func TestDoCloseIfErrorAcceptsNilBody(t *testing.T) { client := mocks.NewClient() SendWithSender(client, mocks.NewRequest(), (func() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { resp, err := s.Do(r) if err != nil { resp.Body.Close() } resp.Body = nil return resp, fmt.Errorf("Faux Error") }) } })(), DoCloseIfError()) } func TestDoErrorIfStatusCode(t *testing.T) { client := mocks.NewClient() client.EmitStatus("400 BadRequest", 400) r, err := SendWithSender(client, mocks.NewRequest(), DoErrorIfStatusCode(400), DoCloseIfError()) if err == nil { t.Error("autorest: DoErrorIfStatusCode failed to emit an error for passed code") } Respond(r, ByClosing()) } func TestDoErrorIfStatusCodeIgnoresStatusCodes(t *testing.T) { client := mocks.NewClient() client.EmitStatus("202 Accepted", 202) r, err := SendWithSender(client, mocks.NewRequest(), DoErrorIfStatusCode(400), DoCloseIfError()) if err != nil { t.Error("autorest: DoErrorIfStatusCode failed to ignore a status code") } Respond(r, ByClosing()) } func TestDoErrorUnlessStatusCode(t *testing.T) { client := mocks.NewClient() client.EmitStatus("400 BadRequest", 400) r, err := SendWithSender(client, mocks.NewRequest(), DoErrorUnlessStatusCode(202), DoCloseIfError()) if err == nil { t.Error("autorest: DoErrorUnlessStatusCode failed to emit an error for an unknown status code") } Respond(r, ByClosing()) } func TestDoErrorUnlessStatusCodeIgnoresStatusCodes(t *testing.T) { client := mocks.NewClient() client.EmitStatus("202 Accepted", 202) r, err := SendWithSender(client, mocks.NewRequest(), DoErrorUnlessStatusCode(202), DoCloseIfError()) if err != nil { t.Error("autorest: DoErrorUnlessStatusCode emitted an error for a knonwn status code") } Respond(r, ByClosing()) } func TestDoRetryForAttemptsStopsAfterAttempts(t *testing.T) { client := mocks.NewClient() client.EmitErrors(10) r, err := SendWithSender(client, mocks.NewRequest(), DoRetryForAttempts(5, time.Duration(0)), DoCloseIfError()) if err == nil { t.Error("autorest: Mock client failed to emit errors") } Respond(r, ByClosing()) if client.Attempts() != 5 { t.Error("autorest: DoRetryForAttempts failed to stop after specified number of attempts") } } func TestDoRetryForAttemptsReturnsResponse(t *testing.T) { client := mocks.NewClient() client.EmitErrors(1) r, err := SendWithSender(client, mocks.NewRequest(), DoRetryForAttempts(1, time.Duration(0))) if err == nil { t.Error("autorest: Mock client failed to emit errors") } if r == nil { t.Error("autorest: DoRetryForAttempts failed to return the underlying response") } Respond(r, ByClosing()) } func TestDoRetryForDurationStopsAfterDuration(t *testing.T) { client := mocks.NewClient() client.EmitErrors(-1) d := 10 * time.Millisecond start := time.Now() r, err := SendWithSender(client, mocks.NewRequest(), DoRetryForDuration(d, time.Duration(0)), DoCloseIfError()) if err == nil { t.Error("autorest: Mock client failed to emit errors") } Respond(r, ByClosing()) if time.Now().Sub(start) < d { t.Error("autorest: DoRetryForDuration failed stopped too soon") } } func TestDoRetryForDurationStopsWithinReason(t *testing.T) { client := mocks.NewClient() client.EmitErrors(-1) d := 10 * time.Millisecond start := time.Now() r, err := SendWithSender(client, mocks.NewRequest(), DoRetryForDuration(d, time.Duration(0)), DoCloseIfError()) if err == nil { t.Error("autorest: Mock client failed to emit errors") } Respond(r, ByClosing()) if time.Now().Sub(start) > (5 * d) { t.Error("autorest: DoRetryForDuration failed stopped soon enough (exceeded 5 times specified duration)") } } func TestDoRetryForDurationReturnsResponse(t *testing.T) { client := mocks.NewClient() client.EmitErrors(-1) r, err := SendWithSender(client, mocks.NewRequest(), DoRetryForDuration(10*time.Millisecond, time.Duration(0)), DoCloseIfError()) if err == nil { t.Error("autorest: Mock client failed to emit errors") } if r == nil { t.Error("autorest: DoRetryForDuration failed to return the underlying response") } Respond(r, ByClosing()) } func TestDelayForBackoff(t *testing.T) { // Establish a baseline and then set the wait to 10x that amount // -- Waiting 10x the baseline should be long enough for a real test while not slowing the // tests down too much tt := time.Now() DelayForBackoff(time.Millisecond, 0) d := 10 * time.Since(tt) start := time.Now() DelayForBackoff(d, 1) if time.Now().Sub(start) < d { t.Error("autorest: DelayForBackoff did not delay as long as expected") } } func TestDelayForBackoffWithinReason(t *testing.T) { // Establish a baseline and then set the wait to 10x that amount // -- Waiting 10x the baseline should be long enough for a real test while not slowing the // tests down too much tt := time.Now() DelayForBackoff(time.Millisecond, 0) d := 10 * time.Since(tt) start := time.Now() DelayForBackoff(d, 1) if time.Now().Sub(start) > (time.Duration(5.0) * d) { t.Error("autorest: DelayForBackoff delayed too long (exceeded 5 times the specified duration)") } } func doEnsureBodyClosed(t *testing.T) SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { resp, err := s.Do(r) if resp != nil && resp.Body != nil && resp.Body.(*mocks.Body).IsOpen() { t.Error("autorest: Expected Body to be closed -- it was left open") } return resp, err }) } } func withMessage(output *string, msg string) SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { resp, err := s.Do(r) if err == nil { *output += msg } return resp, err }) } } go-autorest-0.1-alpha/autorest/utility.go000066400000000000000000000013411256314551300206120ustar00rootroot00000000000000package autorest import ( "fmt" "net/url" ) func containsInt(ints []int, n int) bool { for _, i := range ints { if i == n { return true } } return false } func escapeValueStrings(m map[string]string) map[string]string { for key, value := range m { m[key] = url.QueryEscape(value) } return m } func ensureValueStrings(mapOfInterface map[string]interface{}) map[string]string { mapOfStrings := make(map[string]string) for key, value := range mapOfInterface { mapOfStrings[key] = ensureValueString(value) } return mapOfStrings } func ensureValueString(value interface{}) string { if value == nil { return "" } switch v := value.(type) { case string: return v default: return fmt.Sprintf("%v", v) } } go-autorest-0.1-alpha/autorest/utility_test.go000066400000000000000000000022531256314551300216540ustar00rootroot00000000000000package autorest import ( "reflect" "testing" ) func TestContainsIntFindsValue(t *testing.T) { ints := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} v := 5 if !containsInt(ints, v) { t.Errorf("autorest: containsInt failed to find %v in %v", v, ints) } } func TestContainsIntDoesNotFindValue(t *testing.T) { ints := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} v := 42 if containsInt(ints, v) { t.Errorf("autorest: containsInt unexpectedly found %v in %v", v, ints) } } func TestEscapeStrings(t *testing.T) { m := map[string]string{ "string": "a long string with = odd characters", "int": "42", "nil": "", } r := map[string]string{ "string": "a+long+string+with+%3D+odd+characters", "int": "42", "nil": "", } v := escapeValueStrings(m) if !reflect.DeepEqual(v, r) { t.Errorf("autorest: ensureValueStrings returned %v\n", v) } } func TestEnsureStrings(t *testing.T) { m := map[string]interface{}{ "string": "string", "int": 42, "nil": nil, } r := map[string]string{ "string": "string", "int": "42", "nil": "", } v := ensureValueStrings(m) if !reflect.DeepEqual(v, r) { t.Errorf("autorest: ensureValueStrings returned %v\n", v) } }