Documentation ¶
Overview ¶
Package wrap creates a fast and flexible middleware stack for http.Handlers.
Each middleware is a wrapper for another middleware and implements the Wrapper interface.
Features
- small; core is only 13 LOC
- based on http.Handler interface; nicely integrates with net/http
- middleware stacks are http.Handlers too and may be embedded
- has a solution for per request context sharing
- has debugging helper
- low memory footprint
- fast
- no dependency apart from the standard library
- freely mix middleware with and without context (same interface)
- easy to create adapters / wrappers for 3rd party middleware
Wrappers can be found at http://godoc.org/github.com/go-on/wrap-contrib/wraps.
A (mountable) router that plays fine with wrappers can be found at http://godoc.org/github.com/go-on/router.
Benchmarks (Go 1.3)
// The overhead of n writes to http.ResponseWriter via n wrappers // vs n writes in a loop within a single http.Handler BenchmarkServing2Simple 1000000 1067 ns/op 1,00x BenchmarkServing2Wrappers 1000000 1121 ns/op 1,05x BenchmarkServing50Simple 100000 26041 ns/op 1,00x BenchmarkServing50Wrappers 100000 27053 ns/op 1,04x BenchmarkServing100Simple 50000 52074 ns/op 1,00x BenchmarkServing100Wrappers 50000 53450 ns/op 1,03x
Credits ¶
Initial inspiration came from Christian Neukirchen's rack for ruby some years ago.
Content of the package ¶
The core of this package is the New function that constructs a stack of middlewares that implement the Wrapper interface.
If the global DEBUG flag is set before calling New then each middleware call will result in calling the Debug method of the global DEBUGGER (defaults to a logger).
To help constructing middleware there are some adapters like WrapperFunc, Handler, HandlerFunc, NextHandler and NextHandlerFunc each of them adapting to the Wrapper interface.
To help sharing per request context there is a Contexter interface that must be implemented by the ResponseWriter. That can easily be done be providing a middleware that injects a context that wraps the current ResponseWriter and implements the Contexter interface. It must a least support the extraction of the wrapped ResponseWriter.
Then there are functions to validate implementations of Contexter (ValidateContextInjecter) and to validate them against wrappers that store and retrieve the context data (ValidateWrapperContexts).
An complete example for shared contexts can be found in the file example_context_test.go.
Furthermore this package provides some ResponseWriter wrappers that also implement Contexter and that help with development of middleware.
These are Buffer, Peek and EscapeHTML.
Buffer is a simple buffer. A middleware may pass it to the next handlers ServeHTTP method as a drop in replacement for the response writer. After the ServeHTTP method is run the middleware may examine what has been written to the Buffer and decide what to write to the "original" ResponseWriter (that may well be another buffer passed from another middleware).
The downside is the body being written two times and the complete caching of the body in the memory which will be inacceptable for large bodies.
Therefor Peek is an alternative response writer wrapper that only caching headers and status code but allowing to intercept calls of the Write method. All middleware without the need to read the whole response body should use Peek or provide their own ResponseWriter wrapper (then do not forget to implement the Contexter interface).
Finally EscapeHTML provides a response writer wrapper that allows on the fly html escaping of the bytes written to the wrapped response writer.
How to write a middleware ¶
It is pretty easy to write your custom middleware. You should start with a new struct type - that allows you to add options as fields later on.
Then you could use the following template to implement the Wrapper interface
type MyMiddleware struct { // add your options } // make sure it conforms to the Wrapper interface var _ wrap.Wrapper = MyMiddleware{} // implement the wrapper interface func (m MyMiddleware) Wrap( next http.Handler) http.Handler { var f http.HandlerFunc f = func (rw http.ResponseWriter, req *http.Request) { // do stuff // at some point you might want to run the next handler // if not, your middleware ends the stack chain next.ServeHTTP(rw, req) } return f }
If you need to run the next handler in order to inspect what it did, replace the response writer with a Peek (see NewPeek) or if you need full access to the written body with a Buffer.
How to use middleware ¶
To form a middleware stack, simply use New() and pass it the middlewares. They get the request top-down. There are some adapters to let for example a http.Handler be a middleware (Wrapper) that does not call the next handler and stops the chain.
stack := wrap.New( MyMiddleware{}, OtherMiddleware{}, wrap.Handler(aHandler), ) // stack is now a http.Handler
How to write a middleware that uses per request context ¶
To use per request context a custom type is needed that carries the context data and the user is expected to create and inject a Contexter supporting this type.
Here is a template for a middleware that you could use to write middleware that wants to use / share context.
// MyMiddleware expects the ResponseWriter to implement wrap.Contexter and // to support storing and retrieving the MyContextData type. type MyMiddleware struct { // add your options } // define whatever type you like, but define a type // for each kind of context data you will want to store/retrieve type MyContextData string // make sure it conforms to the ContextWrapper interface var _ wrap.ContextWrapper = MyMiddleware{} // implements ContextWrapper; panics if Contexter does not support // the needed type func (m MyMiddleware) ValidateContext( ctx wrap.Contexter ) { var m MyContextData // try the getter and setter, they will panic if they don't support the type ctx.Context(&m); ctx.SetContext(&m) // do this for every type you need } // implement the wrapper interface func (m MyMiddleware) Wrap( next http.Handler) http.Handler { var f http.HandlerFunc f = func (rw http.ResponseWriter, req *http.Request) { ctx := rw.(wrap.Contexter) m := MyContextData("Hello World") ctx.SetContext(&m) // always pass the pointer var n MyContextData ctx.Context(&n) // n now is MyContextData("Hello World") ... do stuff next.ServeHTTP(rw, req) } return f }
How to use middleware that uses per request context ¶
For context sharing the user has to implement the Contexter interface in a way that supports all types the used middlewares expect.
Here is a template for an implementation of the Contexter interface
type MyContext struct { http.ResponseWriter // you always need this myContextData *myMiddleware.MyContextData // a property for each supported type } // make sure it is a valid context, i.e. http.ResponseWriter is supported by Context // method, the correct types are returned and the panic types are correct var _ = wrap.ValidateContextInjecter(&MyContext{}) // retrieves the value of the type to which ctxPtr is a pointer to func (c *MyContext) Context(ctxPtr interface{}) (found bool) { found = true // save work switch ty := ctxPtr.(type) { // always support http.ResponseWriter in Context method case *http.ResponseWriter: *ty = c.ResponseWriter // add such a case for each supported type case *myMiddleware.MyContextData: if c.myContextData == nil { return false } *ty = *c.myContextData default: // always panic with wrap.ErrUnsupportedContextGetter in Context method on default panic(&wrap.ErrUnsupportedContextGetter{ctxPtr}) } return } // sets the context of the given type func (c *MyContext) SetContext(ctxPtr interface{}) { switch ty := ctxPtr.(type) { case *myMiddleware.MyContextData: c.myContextData = ty default: // always panic with wrap.ErrUnsupportedContextSetter in SetContext method on default panic(&wrap.ErrUnsupportedContextSetter{ctxPtr}) } } // Wrap implements the wrap.Wrapper interface by wrapping a ResponseWriter inside a new // &MyContext and injecting it into the middleware chain. func (c MyContext) Wrap(next http.Handler) http.Handler { var f http.HandlerFunc f = func(rw http.ResponseWriter, req *http.Request) { next.ServeHTTP(&MyContext{ResponseWriter: rw}, req) } return f }
At any time there must be only one Contexter in the whole middleware stack and its the best to let it be the first middleware. Then you don't have to worry if its there or not (the Stack function might help you).
The corresponding middleware stack would look like this
// first check if the Contexter supports all context types needed by the middlewares // this uses the ValidateContext() method of the middlewares that uses context. // It panics on errors. wrap.ValidateWrapperContexts(&MyContext{}, MyMiddleware{}, OtherMiddleware{}) stack := wrap.New( MyContext{}, // injects the &MyContext{} wrapper, should be done at the beginning MyMiddleware{}, OtherMiddleware{}, wrap.Handler(aHandler), ) // stack is now a http.Handler
If your application / handler also uses context data, it is a good idea to implement it as ContextWrapper as if it were a middleware and pass it ValidateWrapperContexts(). So if your context is wrong, you will get nice panics before your server even starts. And this is always in sync with your app / middleware.
If for some reason the original ResponseWriter is needed (to type assert it to a http.Flusher for example), it may be reclaimed with the help of ReclaimResponseWriter().
You might want to look at existing middlewares to get some ideas: http://godoc.org/github.com/go-on/wrap-contrib/wraps
FAQ ¶
1. Should the context not better be an argument to a middleware function, to make this dependency visible in documentation and tools?
Answer: A unified interface gives great freedom when organizing and ordering middleware as the middleware in between does not have to know about a certain context type if does not care about it. It simply passes the ResponseWriter that happens to have context down the middleware stack chain.
On the other hand, exposing the context type by making it a parameter has the effect that every middleware will need to pass this parameter to the next one if the next one needs it. Every middleware is then tightly coupled to the next one and reordering or mixing of middleware from different sources becomes impossible.
However with the ContextHandler interface and the ValidateWrapperContexts function we have a way to guarantee that the requirements are met. And this solution is also type safe.
2. A ResponseWriter is an interface, because it may implement other interfaces from the http libary, e.g. http.Flusher. If it is wrapped that underlying implementation is not accessible anymore
Answer: When the Contexter is validated, it is checked, that the Context method supports http.ResponseWriter as well (and that it returns the underlying ResponseWriter). Since only one Contexter may be used within a stack, it is always possible to ask the Contexter for the underlying ResponseWriter. This is what helper functions like ReclaimResponseWriter(), Flush(), CloseNotify() and Hijack() do.
3. Why is the recommended way to use the Contexter interface to make a type assertion from the ResponseWriter to the Contexter interface without error handling?
Answer: Middleware stacks should be created before the server is handling requests and then not change anymore. And you should run tests. Then the assertion will blow up and that is correct because there is no reasonable way to handle such an error. It means that either you have no context in your stack or you inject the context to late or the context does not handle the kind of type the middleware expects. In each case you should fix it early and the panic forces you to do. Use the DEBUG flag to see what's going on.
4. What happens if my context is wrapped inside another context or response writer?
Answer: You should only have one Contexter per application and inject it as first wrapper into your middleware stack. All context specific data belongs there. Having multiple Contexter in a stack is considered a bug.
The good news is that you now can be sure to be able to access all of your context data everywhere inside your stack.
Never should a context wrap another one. There also should be no need for another context wrapping ResponseWriter because every type can be saved by a Contexter with few code.
All response writer wrappers of this package implement the Contexter interface and every response writer you use should.
5. Why isn't there any default context object? Why do I have to write it on my own?
Answer: To write your own context and context injection has several benefits:
- no magic or reflection necessary
- all your context data is managed at one place
- context data may be generated/updated based on other context data
- your context management is independant from the middleware
6. Why is the context data accessed and stored via type switch and not by string keys?
Answer: Type based context allows a very simple implementation that namespaces across packages needs no extra memory allocation and is, well, type safe. If you need to store multiple context data of the same type, simple defined an alias type for each key.
7. Is there an example how to integrate with 3rd party middleware libraries that expect context?
Answer: Yes, have a look at http://godoc.org/github.com/go-on/wrap-contrib/third-party.
8. What about spawning goroutines / how does it relate to code.google.com/p/go.net/context?
If you need your request to be handled by different goroutines and middlewares you might use your Contexter to store and provide access to a code.google.com/p/go.net/context Context just like any other context data. The good news is that you can write you middleware like before and extract your Context everywhere you need it. And if you have middleware in between that does not know about about Context or other context data you don't have build wrappers around that have Context as parameter. Instead the ResponseWriter that happens to provide you a Context will be passed down the chain by the middleware.
"At Google, we require that Go programmers pass a Context parameter as the first argument to every function on the call path between incoming and outgoing requests."
(Sameer Ajmani, http://blog.golang.org/context)
This is not neccessary anymore. And it is not neccessary for any type of contextual data because that does not have to be in the type signature anymore.
Index ¶
- Variables
- func CloseNotify(rw http.ResponseWriter) (ch <-chan bool, ok bool)
- func Flush(rw http.ResponseWriter) (ok bool)
- func Hijack(rw http.ResponseWriter) (c net.Conn, brw *bufio.ReadWriter, err error, ok bool)
- func New(wrapper ...Wrapper) (h http.Handler)
- func NewLogDebugger(out io.Writer, flag int)
- func ReclaimResponseWriter(rw http.ResponseWriter) http.ResponseWriter
- func SetDebug() bool
- func Stack(inject ContextInjecter, wrapper ...Wrapper) (h http.Handler)
- func ValidateContextInjecter(inject ContextInjecter) bool
- func ValidateWrapperContexts(ctx Contexter, wrapper ...Wrapper)
- type Buffer
- func (bf *Buffer) Body() []byte
- func (bf *Buffer) BodyString() string
- func (bf *Buffer) Context(ctxPtr interface{}) bool
- func (bf *Buffer) FlushAll()
- func (bf *Buffer) FlushCode()
- func (bf *Buffer) FlushHeaders()
- func (bf *Buffer) HasChanged() bool
- func (bf *Buffer) Header() http.Header
- func (bf *Buffer) IsOk() bool
- func (bf *Buffer) Reset()
- func (bf *Buffer) SetContext(ctxPtr interface{})
- func (bf *Buffer) Write(b []byte) (int, error)
- func (bf *Buffer) WriteHeader(i int)
- type ContextInjecter
- type ContextWrapper
- type Contexter
- type Debugger
- type ErrBodyFlushedBeforeCode
- type ErrCodeFlushedBeforeHeaders
- type ErrUnsupportedContextGetter
- type ErrUnsupportedContextSetter
- type EscapeHTML
- type NextHandlerFunc
- type Peek
- func (p *Peek) Context(ctxPtr interface{}) bool
- func (p *Peek) FlushCode()
- func (p *Peek) FlushHeaders()
- func (p *Peek) FlushMissing()
- func (p *Peek) HasChanged() bool
- func (p *Peek) Header() http.Header
- func (p *Peek) IsOk() bool
- func (p *Peek) Reset()
- func (p *Peek) SetContext(ctxPtr interface{})
- func (p *Peek) Write(b []byte) (int, error)
- func (p *Peek) WriteHeader(i int)
- type Wrapper
- type WrapperFunc
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var DEBUG = false
DEBUG indicates if any stack should be debugged. Set it before any call to New.
var DEBUGGER = Debugger(&logDebugger{log.New(os.Stdout, "[go-on/wrap debugger]", log.LstdFlags)})
DEBUGGER is the Debugger used for debugging middleware stacks. It defaults to a logging debugger that logs to os.Stdout
var NoOp = http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})
NoOp is a http.Handler doing nothing
Functions ¶
func CloseNotify ¶
func CloseNotify(rw http.ResponseWriter) (ch <-chan bool, ok bool)
CloseNotify is the same for http.CloseNotifier as Flush is for http.Flusher ok tells if it was a CloseNotifier
func Flush ¶
func Flush(rw http.ResponseWriter) (ok bool)
Flush is a helper that flushes the buffer in the underlying response writer if it is a http.Flusher. The http.ResponseWriter might also be a Contexter if it allows the retrieval of the underlying ResponseWriter. Ok returns if the underlying ResponseWriter was a http.Flusher
func Hijack ¶
func Hijack(rw http.ResponseWriter) (c net.Conn, brw *bufio.ReadWriter, err error, ok bool)
Hijack is the same for http.Hijacker as Flush is for http.Flusher ok tells if it was a Hijacker
func New ¶
New returns the wrapping http.Handler that returned from calling the Wrap method on the first given wrapper that received the returned handler of returning from the second wrappers Wrap method and so on.
The last wrapper begins the loop receiving the NoOp handler.
When the ServeHTTP method of the returned handler is called each wrapping handler may call is next until the NoOp handler is run.
Or some wrapper decides not to call next.ServeHTTP.
If DEBUG is set, each handler is wrapped with a Debug struct that calls DEBUGGER.Debug before running the handler.
Example ¶
package main import ( "fmt" "net/http" ) /* This example illustrates 3 ways to write and use middleware. 1. use a http.Handler as wrapper (needs adapter in the middleware stack) 2. use a ServeHTTPNext method (nice to write but needs adapter in the middleware stack) 3. use a Wrapper (needs no adapter) For sharing context, look at example_context_test.go. */ type print1 string func (p print1) ServeHTTP(wr http.ResponseWriter, req *http.Request) { fmt.Println(p) } func (p print1) ServeHTTPNext(next http.Handler, wr http.ResponseWriter, req *http.Request) { fmt.Print(p) next.ServeHTTP(wr, req) } type print2 string func (p print2) Wrap(next http.Handler) http.Handler { var f http.HandlerFunc f = func(rw http.ResponseWriter, req *http.Request) { fmt.Print(p) next.ServeHTTP(rw, req) } return f } func main() { h := New( NextHandler(print1("ready...")), // make use of ServeHTTPNext method print2("steady..."), // print2 directly fulfills Wrapper interface Handler(print1("go!")), // make use of ServeHTTP method, this stopps the chain // if there should be a handler after this, you will need the Before wrapper from go-on/wrap-contrib/wraps ) r, _ := http.NewRequest("GET", "/", nil) h.ServeHTTP(nil, r) }
Output: ready...steady...go!
func NewLogDebugger ¶
NewLogDebugger sets the DEBUGGER to a logger that logs to the given io.Writer. Flag is a flag from the log standard library that is passed to log.New
func ReclaimResponseWriter ¶
func ReclaimResponseWriter(rw http.ResponseWriter) http.ResponseWriter
ReclaimResponseWriter is a helper that expects the given ResponseWriter to either be the original ResponseWriter or a Contexter which supports getting the original response writer via *http.ResponseWriter. In either case it returns the underlying response writer
func SetDebug ¶
func SetDebug() bool
SetDebug provides a way to set DEBUG=true in a var declaration, like
var _ = wrap.SetDebug()
This is an easy way to ensure DEBUG is set to true before the init functions run
func Stack ¶
func Stack(inject ContextInjecter, wrapper ...Wrapper) (h http.Handler)
Stack creates a stack of middlewares with a context that is injected via inject. After validating the ContextInjecter it adds it at first middleware into the stack and returns the stack built by New This has the effect that the context is injected into the middleware chain at the beginning and every middleware may type assert the ResponseWriter to a Contexter in order to get and set context. Stack panics if inject is not valid. Stack should only be called once per application and must not be embedded into other stacks
func ValidateContextInjecter ¶
func ValidateContextInjecter(inject ContextInjecter) bool
ValidateContextInjecter panics if inject does not inject a Contexter that supports http.ResponseWriter,otherwise it returns true, so you may use it in var declarations that are executed before the init functions
func ValidateWrapperContexts ¶
ValidateWrapperContexts validates the given Contexter against all of the given wrappers that implement the ContextWrapper interface. If every middleware that requires context implements the ContextWrapper interface and is passed to this function, then any missing support for a context type needed by a Wrapper would be uncovered. If then this function is called early it would save many headaches.
Types ¶
type Buffer ¶
type Buffer struct { // ResponseWriter is the underlying response writer that is wrapped by Buffer http.ResponseWriter // Buffer is the underlying io.Writer that buffers the response body Buffer bytes.Buffer // Code is the cached status code Code int // contains filtered or unexported fields }
Buffer is a ResponseWriter wrapper that may be used as buffer.
func NewBuffer ¶
func NewBuffer(w http.ResponseWriter) (bf *Buffer)
NewBuffer creates a new Buffer by wrapping the given response writer.
func (*Buffer) Body ¶
Body returns the bytes of the underlying buffer (that is meant to be the body of the response)
func (*Buffer) BodyString ¶
BodyString returns the string of the underlying buffer (that is meant to be the body of the response)
func (*Buffer) Context ¶
Context gets the context of the underlying response writer. It panics if the underlying response writer does no implement Contexter
func (*Buffer) FlushAll ¶
func (bf *Buffer) FlushAll()
FlushAll flushes headers, status code and body to the underlying ResponseWriter, if something changed
func (*Buffer) FlushCode ¶
func (bf *Buffer) FlushCode()
FlushCode flushes the status code to the underlying responsewriter if it was set.
func (*Buffer) FlushHeaders ¶
func (bf *Buffer) FlushHeaders()
FlushHeaders adds the headers to the underlying ResponseWriter, removing them from Buffer.
func (*Buffer) HasChanged ¶
HasChanged returns true if Header, WriteHeader or Write has been called
func (*Buffer) SetContext ¶
func (bf *Buffer) SetContext(ctxPtr interface{})
SetContext sets the Context of the underlying response writer. It panics if the underlying response writer does no implement Contexter
func (*Buffer) WriteHeader ¶
WriteHeader writes the cached status code and tracks this call as change
type ContextInjecter ¶
type ContextInjecter interface { // Contexter interface must be implemented on a pointer receiver of the struct Contexter // Wrapper interface might be implemented on the struct itself Wrapper }
ContextInjecter injects itself as Contexter into a middleware stack via its Wrapper interface
type ContextWrapper ¶
type ContextWrapper interface { Wrapper // ValidateContext should panic if the given Contexter does not support the required // types ValidateContext(Contexter) }
ContextWrapper is a Wrapper that uses some kind of context It has a Validate() method that panics if the contexts does not support the types that the Wrapper wants to store/retrieve inside the context
type Contexter ¶
type Contexter interface { // since implementations of Context should be a wrapper around a responsewriter // they must implement the http.ResponseWriter interface. http.ResponseWriter // Context lets the given pointer point to the saved context of the same type // Returns if it has found something // It should always support *http.ResponseWriter and set it to the underlying // http.ResponseWriter in order to allow middleware to type assert to Flusher et. al. Context(ctxPtr interface{}) (found bool) // SetContext saves the given context pointer via type switch SetContext(ctxPtr interface{}) }
Contexter is a http.ResponseWriter that can set and get contexts. It allows plain http.Handlers to share per request context data without global state.
Implementations of Context should be structs wrapping a ResponseWriter.
Example ¶
package main import ( "fmt" "net" "net/http" "net/http/httptest" "strings" ) // userIP represents the IP address of a http.Request type userIP net.IP // context implements Contexter, providing a userIP and a error // also implements ContextInjecter to inject itself into the middleware chain type context struct { http.ResponseWriter userIP userIP err error } // make sure to fulfill the ContextInjecter interface var _ ContextInjecter = &context{} var _ = ValidateContextInjecter(&context{}) // context is an implementation for the Contexter interface. // // It receives a pointer to a value that is already stored inside the context. // Values are distiguished by their type. // Context sets the value of the given pointer to the value of the same type // that is stored inside of the context. // A pointer type that is not supported results in a panic. // *http.ResponseWriter should always be supported in order to get the underlying ResponseWriter // Context returns if the pointer is no nil pointer when returning. func (c *context) Context(ctxPtr interface{}) (found bool) { found = true // save work switch ty := ctxPtr.(type) { case *http.ResponseWriter: *ty = c.ResponseWriter case *userIP: if c.userIP == nil { return false } *ty = c.userIP case *error: if c.err == nil { return false } *ty = c.err default: panic(&ErrUnsupportedContextGetter{ctxPtr}) } return } // SetContext is an implementation for the Contexter interface. // // It receives a pointer to a value that will be stored inside the context. // Values are distiguished by their type, that means that SetContext replaces // and stored value of the same type. // A pointer type that is not supported results in a panic. // Supporting the replacement of the underlying response writer is not recommended. func (c *context) SetContext(ctxPtr interface{}) { switch ty := ctxPtr.(type) { case *userIP: c.userIP = *ty case *error: c.err = *ty default: panic(&ErrUnsupportedContextSetter{ctxPtr}) } } // Wrap implements the wrap.Wrapper interface. // // When the request is served, the response writer is wrapped by a // new *context which is passed to the next handlers ServeHTTP method. func (c context) Wrap(next http.Handler) http.Handler { var f http.HandlerFunc f = func(rw http.ResponseWriter, req *http.Request) { next.ServeHTTP(&context{ResponseWriter: rw}, req) } return f } // setUserIP is a middleware that requires a context supporting the userIP and the error type type setUserIP struct{} var _ ContextWrapper = setUserIP{} // ValidateContext makes sure that ctx supports the needed types func (setUserIP) ValidateContext(ctx Contexter) { var userIP userIP var err error // since SetContext should panic for unsupported types, // this should be enough ctx.SetContext(&userIP) ctx.SetContext(&err) } func (setUserIP) Wrap(next http.Handler) http.Handler { var f http.HandlerFunc f = func(rw http.ResponseWriter, req *http.Request) { ip, err := ipfromRequest(req) if err != nil { rw.(Contexter).SetContext(&err) } else { uIP := userIP(ip) rw.(Contexter).SetContext(&uIP) } next.ServeHTTP(rw, req) } return f } // ipfromRequest extracts the user IP address from req, if present. // taken from http://blog.golang.org/context/userip/userip.go (FromRequest) func ipfromRequest(req *http.Request) (net.IP, error) { s := strings.SplitN(req.RemoteAddr, ":", 2) userIP := net.ParseIP(s[0]) if userIP == nil { return nil, fmt.Errorf("userip: %q is not IP:port", req.RemoteAddr) } return userIP, nil } // handleError is a middleware for handling errors. // it requires a context supporting the error type. type handleError struct{} var _ ContextWrapper = handleError{} // Validate makes sure that ctx supports the needed types func (handleError) ValidateContext(ctx Contexter) { var err error // since Context should panic for unsupported types, // this should be enough ctx.Context(&err) } // Wrap implements the wrap.Wrapper interface and checks for an error context. // If it finds one, the status 500 is set and the error is written to the response writer. // If no error is inside the context, the next handler is called. func (handleError) Wrap(next http.Handler) http.Handler { var f http.HandlerFunc f = func(rw http.ResponseWriter, req *http.Request) { var err error rw.(Contexter).Context(&err) if err != nil { rw.WriteHeader(http.StatusInternalServerError) rw.Write([]byte(err.Error())) return } next.ServeHTTP(rw, req) } return f } // app gets the userIP and writes it to the responsewriter. it requires a context supporting the userIP type app struct{} var _ ContextWrapper = app{} // Validate makes sure that ctx supports the needed types func (app) ValidateContext(ctx Contexter) { var uIP userIP // since Context should panic for unsupported types, // this should be enough ctx.Context(&uIP) } // Wrap implements the wrap.Wrapper interface and writes a userIP from a context to the response writer, flushes // it and prints DONE func (app) Wrap(next http.Handler) http.Handler { var f http.HandlerFunc f = func(rw http.ResponseWriter, req *http.Request) { var uIP userIP rw.(Contexter).Context(&uIP) fmt.Fprintln(rw, net.IP(uIP).String()) Flush(rw) fmt.Fprint(rw, "DONE") } return f } func main() { ctx := &context{} // make sure, the context supports all types required by the used middleware ValidateWrapperContexts(ctx, setUserIP{}, handleError{}, app{}) // Stack checks if context is valid (support http.ResponseWriter) // and creates a top level middleware stack (for embedded ones use New()) h := Stack( ctx, // context must always be the first one setUserIP{}, handleError{}, app{}, ) rec := httptest.NewRecorder() r, _ := http.NewRequest("GET", "/", nil) r.RemoteAddr = "garbage" h.ServeHTTP(rec, r) fmt.Println(rec.Body.String()) rec.Body.Reset() r.RemoteAddr = "127.0.0.1:45643" h.ServeHTTP(rec, r) fmt.Println(rec.Body.String()) }
Output: userip: "garbage" is not IP:port 127.0.0.1 DONE
type Debugger ¶
type Debugger interface { // Debug receives the current request, the object that wraps and the role in which // the object acts. Role is a string representing the interface in which obj // is used, e.g. "Wrapper", "http.Handler" and so on Debug(req *http.Request, obj interface{}, role string) }
Debugger has a Debug method to debug middleware stacks
type ErrBodyFlushedBeforeCode ¶
type ErrBodyFlushedBeforeCode struct{}
ErrBodyFlushedBeforeCode is the error returned if a body flushed to an underlying response writer before the status code has been flushed. It should help to sort out errors in middleware that uses responsewriter wrappers from this package.
func (ErrBodyFlushedBeforeCode) Error ¶
func (e ErrBodyFlushedBeforeCode) Error() string
Error returns the error message
type ErrCodeFlushedBeforeHeaders ¶
type ErrCodeFlushedBeforeHeaders struct{}
ErrCodeFlushedBeforeHeaders is the error returned if a status code flushed to an underlying response writer before the headers have been flushed. It should help to sort out errors in middleware that uses responsewriter wrappers from this package.
func (ErrCodeFlushedBeforeHeaders) Error ¶
func (e ErrCodeFlushedBeforeHeaders) Error() string
Error returns the error message
type ErrUnsupportedContextGetter ¶
type ErrUnsupportedContextGetter struct {
Type interface{}
}
ErrUnsupportedContextGetter is the error returned if the context type is not supported by the Context() method of a Contexter
func (*ErrUnsupportedContextGetter) Error ¶
func (e *ErrUnsupportedContextGetter) Error() string
type ErrUnsupportedContextSetter ¶
type ErrUnsupportedContextSetter struct {
Type interface{}
}
ErrUnsupportedContextSetter is the error returned if the context type is not supported by the SetContext() method of a Contexter
func (*ErrUnsupportedContextSetter) Error ¶
func (e *ErrUnsupportedContextSetter) Error() string
type EscapeHTML ¶
type EscapeHTML struct {
http.ResponseWriter
}
EscapeHTML wraps an http.ResponseWriter in order to override its Write method so that it escape html special chars while writing
func (*EscapeHTML) Context ¶
func (e *EscapeHTML) Context(ctxPtr interface{}) bool
Context gets the Context of the underlying response writer. It panics if the underlying response writer does no implement Contexter
func (*EscapeHTML) SetContext ¶
func (e *EscapeHTML) SetContext(ctxPtr interface{})
SetContext sets the Context of the underlying response writer. It panics if the underlying response writer does no implement Contexter
func (*EscapeHTML) Write ¶
func (e *EscapeHTML) Write(b []byte) (num int, err error)
Write writes to the inner *http.ResponseWriter escaping html special chars on the fly Since there is nothing useful to do with the number of bytes written returned from the inner responsewriter, the returned int is always 0. Since there is nothing useful to do in case of a failed write to the response writer, writing errors are silently dropped. the method is modelled after EscapeText from encoding/xml
type NextHandlerFunc ¶
NextHandlerFunc is a Wrapper that is a function handling the request with the aid of the given handler
type Peek ¶
type Peek struct { // the cached status code Code int // the underlying response writer http.ResponseWriter // contains filtered or unexported fields }
Peek is a ResponseWriter wrapper that intercepts the writing of the body, allowing to check headers and status code that has been set to prevent the body writing and to write a modified body.
Peek is more efficient than Buffer, since it does not write to a buffer first before determining if the body will be flushed to the underlying response writer.
func NewPeek ¶
func NewPeek(rw http.ResponseWriter, proceed func(*Peek) bool) *Peek
NewPeek creates a new Peek for the given response writer using the given proceed function.
The proceed function is called when the Write method is run for the first time. It receives the Peek and may check the cached headers and the cached status code.
If the cached headers and the cached status code should be flushed to the underlying response writer, the proceed function must do so (e.g. by calling FlushMissing). This also allows to write other headers status codes or not write them at all.
If the proceed function returns true, the body will be written to the underlying response write. That also holds for all following calls of Write when proceed is not run anymore.
To write some other body or no body at all, proceed must return false, Then after the Write method has been run the Peek might be checked again and the underlying ResponseWriter that is exposed by Peek might be used to write a custom body.
However if the http.Handler that receives the Peek does not write to the body, proceed will not be called at all.
To ensure that any cached headers and status code will be flushed, the FlushMissing method can be called after the serving http.Handler is run.
If proceed is nil, Write behaves as if proceed would have returned true.
func (*Peek) Context ¶
Context gets the Context of the underlying response writer. It panics if the underlying response writer does no implement Context
func (*Peek) FlushCode ¶
func (p *Peek) FlushCode()
FlushCode writes the status code to the underlying responsewriter if it was set
func (*Peek) FlushHeaders ¶
func (p *Peek) FlushHeaders()
FlushHeaders adds the headers to the underlying ResponseWriter, removing them from Peek
func (*Peek) FlushMissing ¶
func (p *Peek) FlushMissing()
FlushMissing ensures that the Headers and Code are written to the underlying ResponseWriter if they are not written yet (and nothing has been written to the body)
func (*Peek) HasChanged ¶
HasChanged returns true if Header or WriteHeader method have been called or if Write has been called and did write to the underlying response writer.
func (*Peek) Reset ¶
func (p *Peek) Reset()
Reset set the Peek to the defaults, so it will act as if it was freshly initialized.
func (*Peek) SetContext ¶
func (p *Peek) SetContext(ctxPtr interface{})
SetContext sets the Context of the underlying response writer. It panics if the underlying response writer does no implement Context
func (*Peek) Write ¶
Write writes to the underlying response writer, if the proceed function returns true. Otherwise it returns 0, io.EOF. If the data is written, the call is tracked as change.
The proceed function is only called the first time, Write has been called. If proceed is nil, it behaves as if proceed would have returned true.
See NewPeek for more informations about the usage of the proceed function.
func (*Peek) WriteHeader ¶
WriteHeader writes the cached status code, tracking the call as change
type Wrapper ¶
type Wrapper interface { // Wrap wraps the next `http.Handler` of the stack and returns a wrapping `http.Handler`. // If it does not call `next.ServeHTTP`, nobody will. Wrap(next http.Handler) http.Handler }
Wrapper can wrap a http.Handler with another one
func Handler ¶
Handler returns a Wrapper for a http.Handler. The returned Wrapper simply runs the given handler and ignores the next handler in the stack.
func HandlerFunc ¶
func HandlerFunc(fn func(http.ResponseWriter, *http.Request)) Wrapper
HandlerFunc is like Handler but for a function with the type signature of http.HandlerFunc
func NextHandler ¶
func NextHandler(sh interface { ServeHTTPNext(next http.Handler, rw http.ResponseWriter, req *http.Request) }) Wrapper
NextHandler returns a Wrapper for an interface with a ServeHTTPNext method