aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/Azure/azure-pipeline-go/pipeline/core.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/Azure/azure-pipeline-go/pipeline/core.go')
-rwxr-xr-xvendor/github.com/Azure/azure-pipeline-go/pipeline/core.go255
1 files changed, 255 insertions, 0 deletions
diff --git a/vendor/github.com/Azure/azure-pipeline-go/pipeline/core.go b/vendor/github.com/Azure/azure-pipeline-go/pipeline/core.go
new file mode 100755
index 000000000..0dde81d72
--- /dev/null
+++ b/vendor/github.com/Azure/azure-pipeline-go/pipeline/core.go
@@ -0,0 +1,255 @@
+package pipeline
+
+import (
+ "context"
+ "net"
+ "net/http"
+ "os"
+ "time"
+)
+
+// The Factory interface represents an object that can create its Policy object. Each HTTP request sent
+// requires that this Factory create a new instance of its Policy object.
+type Factory interface {
+ New(next Policy, po *PolicyOptions) Policy
+}
+
+// FactoryFunc is an adapter that allows the use of an ordinary function as a Factory interface.
+type FactoryFunc func(next Policy, po *PolicyOptions) PolicyFunc
+
+// New calls f(next,po).
+func (f FactoryFunc) New(next Policy, po *PolicyOptions) Policy {
+ return f(next, po)
+}
+
+// The Policy interface represents a mutable Policy object created by a Factory. The object can mutate/process
+// the HTTP request and then forward it on to the next Policy object in the linked-list. The returned
+// Response goes backward through the linked-list for additional processing.
+// NOTE: Request is passed by value so changes do not change the caller's version of
+// the request. However, Request has some fields that reference mutable objects (not strings).
+// These references are copied; a deep copy is not performed. Specifically, this means that
+// you should avoid modifying the objects referred to by these fields: URL, Header, Body,
+// GetBody, TransferEncoding, Form, MultipartForm, Trailer, TLS, Cancel, and Response.
+type Policy interface {
+ Do(ctx context.Context, request Request) (Response, error)
+}
+
+// PolicyFunc is an adapter that allows the use of an ordinary function as a Policy interface.
+type PolicyFunc func(ctx context.Context, request Request) (Response, error)
+
+// Do calls f(ctx, request).
+func (f PolicyFunc) Do(ctx context.Context, request Request) (Response, error) {
+ return f(ctx, request)
+}
+
+// Options configures a Pipeline's behavior.
+type Options struct {
+ HTTPSender Factory // If sender is nil, then the pipeline's default client is used to send the HTTP requests.
+ Log LogOptions
+}
+
+// LogLevel tells a logger the minimum level to log. When code reports a log entry,
+// the LogLevel indicates the level of the log entry. The logger only records entries
+// whose level is at least the level it was told to log. See the Log* constants.
+// For example, if a logger is configured with LogError, then LogError, LogPanic,
+// and LogFatal entries will be logged; lower level entries are ignored.
+type LogLevel uint32
+
+const (
+ // LogNone tells a logger not to log any entries passed to it.
+ LogNone LogLevel = iota
+
+ // LogFatal tells a logger to log all LogFatal entries passed to it.
+ LogFatal
+
+ // LogPanic tells a logger to log all LogPanic and LogFatal entries passed to it.
+ LogPanic
+
+ // LogError tells a logger to log all LogError, LogPanic and LogFatal entries passed to it.
+ LogError
+
+ // LogWarning tells a logger to log all LogWarning, LogError, LogPanic and LogFatal entries passed to it.
+ LogWarning
+
+ // LogInfo tells a logger to log all LogInfo, LogWarning, LogError, LogPanic and LogFatal entries passed to it.
+ LogInfo
+
+ // LogDebug tells a logger to log all LogDebug, LogInfo, LogWarning, LogError, LogPanic and LogFatal entries passed to it.
+ LogDebug
+)
+
+// LogOptions configures the pipeline's logging mechanism & level filtering.
+type LogOptions struct {
+ Log func(level LogLevel, message string)
+
+ // ShouldLog is called periodically allowing you to return whether the specified LogLevel should be logged or not.
+ // An application can return different values over the its lifetime; this allows the application to dynamically
+ // alter what is logged. NOTE: This method can be called by multiple goroutines simultaneously so make sure
+ // you implement it in a goroutine-safe way. If nil, nothing is logged (the equivalent of returning LogNone).
+ // Usually, the function will be implemented simply like this: return level <= LogWarning
+ ShouldLog func(level LogLevel) bool
+}
+
+type pipeline struct {
+ factories []Factory
+ options Options
+}
+
+// The Pipeline interface represents an ordered list of Factory objects and an object implementing the HTTPSender interface.
+// You construct a Pipeline by calling the pipeline.NewPipeline function. To send an HTTP request, call pipeline.NewRequest
+// and then call Pipeline's Do method passing a context, the request, and a method-specific Factory (or nil). Passing a
+// method-specific Factory allows this one call to Do to inject a Policy into the linked-list. The policy is injected where
+// the MethodFactoryMarker (see the pipeline.MethodFactoryMarker function) is in the slice of Factory objects.
+//
+// When Do is called, the Pipeline object asks each Factory object to construct its Policy object and adds each Policy to a linked-list.
+// THen, Do sends the Context and Request through all the Policy objects. The final Policy object sends the request over the network
+// (via the HTTPSender object passed to NewPipeline) and the response is returned backwards through all the Policy objects.
+// Since Pipeline and Factory objects are goroutine-safe, you typically create 1 Pipeline object and reuse it to make many HTTP requests.
+type Pipeline interface {
+ Do(ctx context.Context, methodFactory Factory, request Request) (Response, error)
+}
+
+// NewPipeline creates a new goroutine-safe Pipeline object from the slice of Factory objects and the specified options.
+func NewPipeline(factories []Factory, o Options) Pipeline {
+ if o.HTTPSender == nil {
+ o.HTTPSender = newDefaultHTTPClientFactory()
+ }
+ if o.Log.Log == nil {
+ o.Log.Log = func(LogLevel, string) {} // No-op logger
+ }
+ return &pipeline{factories: factories, options: o}
+}
+
+// Do is called for each and every HTTP request. It tells each Factory to create its own (mutable) Policy object
+// replacing a MethodFactoryMarker factory (if it exists) with the methodFactory passed in. Then, the Context and Request
+// are sent through the pipeline of Policy objects (which can transform the Request's URL/query parameters/headers) and
+// ultimately sends the transformed HTTP request over the network.
+func (p *pipeline) Do(ctx context.Context, methodFactory Factory, request Request) (Response, error) {
+ response, err := p.newPolicies(methodFactory).Do(ctx, request)
+ request.close()
+ return response, err
+}
+
+func (p *pipeline) newPolicies(methodFactory Factory) Policy {
+ // The last Policy is the one that actually sends the request over the wire and gets the response.
+ // It is overridable via the Options' HTTPSender field.
+ po := &PolicyOptions{pipeline: p} // One object shared by all policy objects
+ next := p.options.HTTPSender.New(nil, po)
+
+ // Walk over the slice of Factory objects in reverse (from wire to API)
+ markers := 0
+ for i := len(p.factories) - 1; i >= 0; i-- {
+ factory := p.factories[i]
+ if _, ok := factory.(methodFactoryMarker); ok {
+ markers++
+ if markers > 1 {
+ panic("MethodFactoryMarker can only appear once in the pipeline")
+ }
+ if methodFactory != nil {
+ // Replace MethodFactoryMarker with passed-in methodFactory
+ next = methodFactory.New(next, po)
+ }
+ } else {
+ // Use the slice's Factory to construct its Policy
+ next = factory.New(next, po)
+ }
+ }
+
+ // Each Factory has created its Policy
+ if markers == 0 && methodFactory != nil {
+ panic("Non-nil methodFactory requires MethodFactoryMarker in the pipeline")
+ }
+ return next // Return head of the Policy object linked-list
+}
+
+// A PolicyOptions represents optional information that can be used by a node in the
+// linked-list of Policy objects. A PolicyOptions is passed to the Factory's New method
+// which passes it (if desired) to the Policy object it creates. Today, the Policy object
+// uses the options to perform logging. But, in the future, this could be used for more.
+type PolicyOptions struct {
+ pipeline *pipeline
+}
+
+// ShouldLog returns true if the specified log level should be logged.
+func (po *PolicyOptions) ShouldLog(level LogLevel) bool {
+ if po.pipeline.options.Log.ShouldLog != nil {
+ return po.pipeline.options.Log.ShouldLog(level)
+ }
+ return false
+}
+
+// Log logs a string to the Pipeline's Logger.
+func (po *PolicyOptions) Log(level LogLevel, msg string) {
+ if !po.ShouldLog(level) {
+ return // Short circuit message formatting if we're not logging it
+ }
+
+ // We are logging it, ensure trailing newline
+ if len(msg) == 0 || msg[len(msg)-1] != '\n' {
+ msg += "\n" // Ensure trailing newline
+ }
+ po.pipeline.options.Log.Log(level, msg)
+
+ // If logger doesn't handle fatal/panic, we'll do it here.
+ if level == LogFatal {
+ os.Exit(1)
+ } else if level == LogPanic {
+ panic(msg)
+ }
+}
+
+var pipelineHTTPClient = newDefaultHTTPClient()
+
+func newDefaultHTTPClient() *http.Client {
+ // We want the Transport to have a large connection pool
+ return &http.Client{
+ Transport: &http.Transport{
+ Proxy: http.ProxyFromEnvironment,
+ // We use Dial instead of DialContext as DialContext has been reported to cause slower performance.
+ Dial /*Context*/ : (&net.Dialer{
+ Timeout: 30 * time.Second,
+ KeepAlive: 30 * time.Second,
+ DualStack: true,
+ }).Dial, /*Context*/
+ MaxIdleConns: 0, // No limit
+ MaxIdleConnsPerHost: 100,
+ IdleConnTimeout: 90 * time.Second,
+ TLSHandshakeTimeout: 10 * time.Second,
+ ExpectContinueTimeout: 1 * time.Second,
+ DisableKeepAlives: false,
+ DisableCompression: false,
+ MaxResponseHeaderBytes: 0,
+ //ResponseHeaderTimeout: time.Duration{},
+ //ExpectContinueTimeout: time.Duration{},
+ },
+ }
+}
+
+// newDefaultHTTPClientFactory creates a DefaultHTTPClientPolicyFactory object that sends HTTP requests to a Go's default http.Client.
+func newDefaultHTTPClientFactory() Factory {
+ return FactoryFunc(func(next Policy, po *PolicyOptions) PolicyFunc {
+ return func(ctx context.Context, request Request) (Response, error) {
+ r, err := pipelineHTTPClient.Do(request.WithContext(ctx))
+ if err != nil {
+ err = NewError(err, "HTTP request failed")
+ }
+ return NewHTTPResponse(r), err
+ }
+ })
+}
+
+var mfm = methodFactoryMarker{} // Singleton
+
+// MethodFactoryMarker returns a special marker Factory object. When Pipeline's Do method is called, any
+// MethodMarkerFactory object is replaced with the specified methodFactory object. If nil is passed fro Do's
+// methodFactory parameter, then the MethodFactoryMarker is ignored as the linked-list of Policy objects is created.
+func MethodFactoryMarker() Factory {
+ return mfm
+}
+
+type methodFactoryMarker struct {
+}
+
+func (methodFactoryMarker) New(next Policy, po *PolicyOptions) Policy {
+ panic("methodFactoryMarker policy should have been replaced with a method policy")
+}