This commit is contained in:
Jeffrey Richter 2017-12-05 08:20:58 -08:00
Родитель bb9a4c3572
Коммит ba7744adfa
3 изменённых файлов: 39 добавлений и 42 удалений

Просмотреть файл

@ -11,7 +11,7 @@ import (
// The Factory interface represents an object that can create its Policy object. Each HTTP request sent // 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. // requires that this Factory create a new instance of its Policy object.
type Factory interface { type Factory interface {
New(node Node) Policy New(next Policy, config *Configuration) Policy
} }
// The Policy interface represents a mutable Policy object created by a Factory. The object can mutate/process // The Policy interface represents a mutable Policy object created by a Factory. The object can mutate/process
@ -113,8 +113,8 @@ func (p *pipeline) Do(ctx context.Context, methodFactory Factory, request Reques
func (p *pipeline) newPolicies(methodFactory Factory) Policy { 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. // 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. // It is overridable via the Options' HTTPSender field.
node := Node{pipeline: p, next: nil} config := &Configuration{pipeline: p} // One object shared by all policy objects
node.next = p.options.HTTPSender.New(node) next := p.options.HTTPSender.New(nil, config)
// Walk over the slice of Factory objects in reverse (from wire to API) // Walk over the slice of Factory objects in reverse (from wire to API)
markers := 0 markers := 0
@ -127,11 +127,11 @@ func (p *pipeline) newPolicies(methodFactory Factory) Policy {
} }
if methodFactory != nil { if methodFactory != nil {
// Replace MethodFactoryMarker with passed-in methodFactory // Replace MethodFactoryMarker with passed-in methodFactory
node.next = methodFactory.New(node) next = methodFactory.New(next, config)
} }
} else { } else {
// Use the slice's Factory to construct its Policy // Use the slice's Factory to construct its Policy
node.next = factory.New(node) next = factory.New(next, config)
} }
} }
@ -139,39 +139,32 @@ func (p *pipeline) newPolicies(methodFactory Factory) Policy {
if markers == 0 && methodFactory != nil { if markers == 0 && methodFactory != nil {
panic("Non-nil methodFactory requires MethodFactoryMarker in the pipeline") panic("Non-nil methodFactory requires MethodFactoryMarker in the pipeline")
} }
return node.next // Return head of the Policy object linked-list return next // Return head of the Policy object linked-list
} }
// A Node represents a node in a linked-list of Policy objects. A Node is passed // A Configuration represents additional configuration information that can optionally be used
// to the Factory's New method which passes to the Policy object it creates. The Policy object // by a node in the linked-list of Policy objects. A Configuration is passed to the Factory's
// uses the Node to forward the Context and HTTP request to the next Policy object in the pipeline. // New method which passes it (if desired) to the Policy object it creates. Today, the Policy object
type Node struct { // uses the Configuration to perform logging. But, in the future, this could be used for more.
type Configuration struct {
pipeline *pipeline pipeline *pipeline
next Policy
}
// Do forwards the Context and HTTP request to the next Policy object in the pipeline. The last Policy object
// sends the request over the network via HTTPSender's Do method. The response and error are returned
// back up the pipeline through the Policy objects.
func (n *Node) Do(ctx context.Context, request Request) (Response, error) {
return n.next.Do(ctx, request)
} }
// ShouldLog returns true if the specified log level should be logged. // ShouldLog returns true if the specified log level should be logged.
func (n *Node) ShouldLog(level LogLevel) bool { func (c *Configuration) ShouldLog(level LogLevel) bool {
if level == LogNone { if level == LogNone {
return false return false
} }
minimumLevel := LogNone minimumLevel := LogNone
if n.pipeline.options.Log.MinimumLevelToLog != nil { if c.pipeline.options.Log.MinimumLevelToLog != nil {
minimumLevel = n.pipeline.options.Log.MinimumLevelToLog() minimumLevel = c.pipeline.options.Log.MinimumLevelToLog()
} }
return level <= minimumLevel return level <= minimumLevel
} }
// Log logs a string to the Pipeline's Logger. // Log logs a string to the Pipeline's Logger.
func (n *Node) Log(level LogLevel, msg string) { func (c *Configuration) Log(level LogLevel, msg string) {
if !n.ShouldLog(level) { if !c.ShouldLog(level) {
return // Short circuit message formatting if we're not logging it return // Short circuit message formatting if we're not logging it
} }
@ -179,7 +172,7 @@ func (n *Node) Log(level LogLevel, msg string) {
if len(msg) == 0 || msg[len(msg)-1] != '\n' { if len(msg) == 0 || msg[len(msg)-1] != '\n' {
msg += "\n" // Ensure trailing newline msg += "\n" // Ensure trailing newline
} }
n.pipeline.options.Log.Log(level, msg) c.pipeline.options.Log.Log(level, msg)
// If logger doesn't handle fatal/panic, we'll do it here. // If logger doesn't handle fatal/panic, we'll do it here.
if level == LogFatal { if level == LogFatal {
@ -224,12 +217,12 @@ type defaultHTTPClientPolicyFactory struct {
} }
// Create initializes a logging policy object. // Create initializes a logging policy object.
func (f *defaultHTTPClientPolicyFactory) New(node Node) Policy { func (f *defaultHTTPClientPolicyFactory) New(next Policy, config *Configuration) Policy {
return &defaultHTTPClientPolicy{node: node} return &defaultHTTPClientPolicy{config: config}
} }
type defaultHTTPClientPolicy struct { type defaultHTTPClientPolicy struct {
node Node config *Configuration
} }
func (p *defaultHTTPClientPolicy) Do(ctx context.Context, request Request) (Response, error) { func (p *defaultHTTPClientPolicy) Do(ctx context.Context, request Request) (Response, error) {
@ -252,6 +245,6 @@ func MethodFactoryMarker() Factory {
type methodFactoryMarker struct { type methodFactoryMarker struct {
} }
func (mpmf methodFactoryMarker) New(node Node) Policy { func (mpmf methodFactoryMarker) New(next Policy, config *Configuration) Policy {
panic("methodFactoryMarker policy should have been replaced with a method policy") panic("methodFactoryMarker policy should have been replaced with a method policy")
} }

Просмотреть файл

@ -18,12 +18,13 @@ type myPolicyFactory struct {
} }
// New initializes a Xxx policy object. // New initializes a Xxx policy object.
func (f *myPolicyFactory) New(node Node) Policy { func (f *myPolicyFactory) New(next Policy, config *Configuration) Policy {
return &myPolicy{node: node /* Set desired fields */} return &myPolicy{next: next, config: config /* Set desired fields */}
} }
type myPolicy struct { type myPolicy struct {
node Node // Mandatory private field next Policy
config *Configuration // Mandatory private field
// Additional desired fields (mutable for use by this specific Policy object) // Additional desired fields (mutable for use by this specific Policy object)
} }
@ -34,7 +35,7 @@ func (p *myPolicy) Do(ctx context.Context, request Request) (response Response,
// You can also pass a different Context on. // You can also pass a different Context on.
// Forward the request to the next node in the pipeline: // Forward the request to the next node in the pipeline:
response, err = p.node.Do(ctx, request) response, err = p.next.Do(ctx, request)
// Process the response here. You can deserialize the body into an object. // Process the response here. You can deserialize the body into an object.
// If you do this, also define a struct that wraps an http.Response & your // If you do this, also define a struct that wraps an http.Response & your

Просмотреть файл

@ -34,18 +34,21 @@ func (r httpResponse) Response() *http.Response {
return r.response return r.response
} }
// WriteRequest appends a formatted HTTP request into a Buffer. // WriteRequestWithResponse appends a formatted HTTP request into a Buffer. If request and/or err are
func WriteRequest(b *bytes.Buffer, request *http.Request) { // not nil, then these are also written into the Buffer.
func WriteRequestWithResponse(b *bytes.Buffer, request *http.Request, response *http.Response, err error) {
// Write the request into the buffer.
fmt.Fprint(b, " "+request.Method+" "+request.URL.String()+"\n") fmt.Fprint(b, " "+request.Method+" "+request.URL.String()+"\n")
writeHeader(b, request.Header) writeHeader(b, request.Header)
} if response != nil {
fmt.Fprintln(b, " --------------------------------------------------------------------------------")
// WriteRequestWithResponse appends a formatted HTTP response with its initiating request into a Buffer. fmt.Fprintf(b, " RESPONSE Status: %s\n", response.Status)
func WriteRequestWithResponse(b *bytes.Buffer, request *http.Request, response *http.Response) { writeHeader(b, response.Header)
WriteRequest(b, request) // Write the request first followed by the response. }
fmt.Fprintln(b, " --------------------------------------------------------------------------------") if err != nil {
fmt.Fprintf(b, " RESPONSE Status: %s\n", response.Status) fmt.Fprintln(b, " --------------------------------------------------------------------------------")
writeHeader(b, response.Header) fmt.Fprintf(b, " ERROR:\n%v\n", err)
}
} }
// formatHeaders appends an HTTP request's or response's header into a Buffer. // formatHeaders appends an HTTP request's or response's header into a Buffer.