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
// requires that this Factory create a new instance of its Policy object.
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
@ -113,8 +113,8 @@ func (p *pipeline) Do(ctx context.Context, methodFactory Factory, request Reques
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.
node := Node{pipeline: p, next: nil}
node.next = p.options.HTTPSender.New(node)
config := &Configuration{pipeline: p} // One object shared by all policy objects
next := p.options.HTTPSender.New(nil, config)
// Walk over the slice of Factory objects in reverse (from wire to API)
markers := 0
@ -127,11 +127,11 @@ func (p *pipeline) newPolicies(methodFactory Factory) Policy {
}
if methodFactory != nil {
// Replace MethodFactoryMarker with passed-in methodFactory
node.next = methodFactory.New(node)
next = methodFactory.New(next, config)
}
} else {
// 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 {
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
// to the Factory's New method which passes to the Policy object it creates. The Policy object
// uses the Node to forward the Context and HTTP request to the next Policy object in the pipeline.
type Node struct {
// A Configuration represents additional configuration information that can optionally be used
// by a node in the linked-list of Policy objects. A Configuration 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 Configuration to perform logging. But, in the future, this could be used for more.
type Configuration struct {
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.
func (n *Node) ShouldLog(level LogLevel) bool {
func (c *Configuration) ShouldLog(level LogLevel) bool {
if level == LogNone {
return false
}
minimumLevel := LogNone
if n.pipeline.options.Log.MinimumLevelToLog != nil {
minimumLevel = n.pipeline.options.Log.MinimumLevelToLog()
if c.pipeline.options.Log.MinimumLevelToLog != nil {
minimumLevel = c.pipeline.options.Log.MinimumLevelToLog()
}
return level <= minimumLevel
}
// Log logs a string to the Pipeline's Logger.
func (n *Node) Log(level LogLevel, msg string) {
if !n.ShouldLog(level) {
func (c *Configuration) Log(level LogLevel, msg string) {
if !c.ShouldLog(level) {
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' {
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 level == LogFatal {
@ -224,12 +217,12 @@ type defaultHTTPClientPolicyFactory struct {
}
// Create initializes a logging policy object.
func (f *defaultHTTPClientPolicyFactory) New(node Node) Policy {
return &defaultHTTPClientPolicy{node: node}
func (f *defaultHTTPClientPolicyFactory) New(next Policy, config *Configuration) Policy {
return &defaultHTTPClientPolicy{config: config}
}
type defaultHTTPClientPolicy struct {
node Node
config *Configuration
}
func (p *defaultHTTPClientPolicy) Do(ctx context.Context, request Request) (Response, error) {
@ -252,6 +245,6 @@ func MethodFactoryMarker() Factory {
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")
}

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

@ -18,12 +18,13 @@ type myPolicyFactory struct {
}
// New initializes a Xxx policy object.
func (f *myPolicyFactory) New(node Node) Policy {
return &myPolicy{node: node /* Set desired fields */}
func (f *myPolicyFactory) New(next Policy, config *Configuration) Policy {
return &myPolicy{next: next, config: config /* Set desired fields */}
}
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)
}
@ -34,7 +35,7 @@ func (p *myPolicy) Do(ctx context.Context, request Request) (response Response,
// You can also pass a different Context on.
// 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.
// 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
}
// WriteRequest appends a formatted HTTP request into a Buffer.
func WriteRequest(b *bytes.Buffer, request *http.Request) {
// WriteRequestWithResponse appends a formatted HTTP request into a Buffer. If request and/or err are
// 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")
writeHeader(b, request.Header)
}
// WriteRequestWithResponse appends a formatted HTTP response with its initiating request into a Buffer.
func WriteRequestWithResponse(b *bytes.Buffer, request *http.Request, response *http.Response) {
WriteRequest(b, request) // Write the request first followed by the response.
fmt.Fprintln(b, " --------------------------------------------------------------------------------")
fmt.Fprintf(b, " RESPONSE Status: %s\n", response.Status)
writeHeader(b, response.Header)
if response != nil {
fmt.Fprintln(b, " --------------------------------------------------------------------------------")
fmt.Fprintf(b, " RESPONSE Status: %s\n", response.Status)
writeHeader(b, response.Header)
}
if err != nil {
fmt.Fprintln(b, " --------------------------------------------------------------------------------")
fmt.Fprintf(b, " ERROR:\n%v\n", err)
}
}
// formatHeaders appends an HTTP request's or response's header into a Buffer.