Skip to content

Instantly share code, notes, and snippets.

@jordanorelli
Created February 17, 2017 21:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jordanorelli/8609452fef3cc1056339c3747cadf082 to your computer and use it in GitHub Desktop.
Save jordanorelli/8609452fef3cc1056339c3747cadf082 to your computer and use it in GitHub Desktop.
package main
import (
"html/template"
"log"
"net/http"
// "sync"
// "unsafe"
)
var sharedData pageData
var page = template.Must(template.New("page").Parse(`
<html>
<head></head>
<body>
Handler Count: {{ .HandlerCount }} <br />
Middleware Count: {{ .MiddlewareCount }} <br />
<ul>
<li><a href="/shared">shared</a></li>
<li><a href="/enclose-value">enclose value</a></li>
<li><a href="/other-enclose-value">other enclose value</a></li>
<li><a href="/enclose-pointer">enclose pointer</a></li>
<li><a href="/other-enclose-pointer">other enclose pointer</a></li>
<li><a href="/val-handler">val handler</a></li>
<li><a href="/val-handler-ptr">val handler pointer</a></li>
<li><a href="/ptr-handler">pointer handler</a></li>
<li><a href="/fn-wrap/val-handler">fn { val handler }</a></li>
<li><a href="/fn-wrap/val-handler-ptr">fn { val handler pointer }</a></li>
<li><a href="/fn-wrap/ptr-handler">fn { pointer handler }</a></li>
<li><a href="/val-wrap/val-handler">val { val handler }</a></li>
<li><a href="/val-wrap/val-handler-ptr">val { val handler pointer }</a></li>
<li><a href="/val-wrap/ptr-handler">val { pointer handler }</a></li>
<li><a href="/val-wrap-ptr/val-handler">val pointer { val handler }</a></li>
<li><a href="/val-wrap-ptr/val-handler-ptr">val pointer { val handler pointer }</a></li>
<li><a href="/val-wrap-ptr/ptr-handler">val pointer { pointer handler }</a></li>
<li><a href="/ptr-wrap/val-handler">pointer { val handler }</a></li>
<li><a href="/ptr-wrap/val-handler-ptr">pointer { val handler pointer }</a></li>
<li><a href="/ptr-wrap/ptr-handler">pointer { pointer handler }</a></li>
</ul>
</body>
</html>
`))
type pageData struct {
HandlerCount int
MiddlewareCount int
}
func (d *pageData) nextHandlerVal() { d.HandlerCount += 1 }
func (d *pageData) setMiddlewareVal(v int) { d.MiddlewareCount = v }
// shared is a function that can be used in http.HandleFunc or converted to an
// http.HandlerFunc that accesses shared data. Every request has access to the
// same shared data.
func shared(w http.ResponseWriter, r *http.Request) {
log.Printf("%s: shared has initial pageData of %v", r.URL.Path, sharedData)
sharedData.nextHandlerVal()
log.Printf("%s: shared has mutated pageData of %v", r.URL.Path, sharedData)
page.Execute(w, sharedData)
}
// encloseValue closes over a value and yields a handler. Every request
// processed by that handler will have access to the same closed-over value.
// Mutations to this value will be visible across http request boundaries.
func encloseValue(d pageData) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("%s: encloseValue has initial pageData of %v", r.URL.Path, d)
d.nextHandlerVal()
log.Printf("%s: encloseValue has mutated pageData of %v", r.URL.Path, d)
page.Execute(w, d)
})
}
// enclosePointer closes over a pointer and yields a handler. Every request
// proccessed by that handler will have access to the same closed-over pointer.
// Mutations to this data will be visible across http request boundaries.
func enclosePointer(d *pageData) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("%s: enclosePointer has initial pageData of %v", r.URL.Path, d)
d.nextHandlerVal()
log.Printf("%s: enclosePointer has mutated pageData of %v", r.URL.Path, d)
page.Execute(w, d)
})
}
// valHandler is a type of http.Handler that implements its ServeHTTP method
// with a value receiver. Because the receiver is a value type, each request is
// processed with its own copy of valHandler. The fields on valHandler can thus
// be described as non-shared data, because the data is not shared across an
// http request boundary. Any mutations made to the valHandler from within
// ServeHTTP will not be visible across http request boundaries.
type valHandler struct {
pageData
}
func (h valHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Printf("%s: valHandler has initial HandlerCount of %d", r.URL.Path, h.HandlerCount)
h.nextHandlerVal()
log.Printf("%s: valHandler has mutated HandlerCount of %d", r.URL.Path, h.HandlerCount)
page.Execute(w, h)
}
// ptrHandler is a type of http.Handler that implements its ServeHTTP method
// with a pointer receiver. Because the receiver is a pointer type, each
// request is processed with its own copy of that pointer. That is, each
// request has a pointer that points to the same piece of data. In this way,
// each request is processed with shared data. Mutations to this shared data
// will be visible across http request boundaries.
type ptrHandler struct {
pageData
}
func (h *ptrHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Printf("%s: ptrHandler has initial HandlerCount of %d", r.URL.Path, h.HandlerCount)
h.nextHandlerVal()
log.Printf("%s: ptrHandler has mutated HandlerCount of %d", r.URL.Path, h.HandlerCount)
page.Execute(w, h)
}
type counter interface {
http.Handler
setMiddlewareVal(int)
}
func fnWrapper(h http.Handler) http.Handler {
count := 0
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if v, ok := h.(counter); ok {
log.Printf("%s: fnWrapper wrapping handler that IS counter", r.URL.Path)
log.Printf("%s: fnWrapper has initial count of %d", r.URL.Path, count)
count += 1
log.Printf("%s: fnWrapper has mutated count of %d", r.URL.Path, count)
v.setMiddlewareVal(count)
v.ServeHTTP(w, r)
} else {
log.Printf("%s: fnWrapper wrapping handler that is NOT counter", r.URL.Path)
h.ServeHTTP(w, r)
}
})
}
type valWrapper struct {
count int
Handler http.Handler
}
func (wrap valWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if v, ok := wrap.Handler.(counter); ok {
log.Printf("%s: valWrapper wrapping handler that IS counter", r.URL.Path)
log.Printf("%s: valWrapper has initial count of %d", r.URL.Path, wrap.count)
wrap.count += 1
log.Printf("%s: valWrapper has mutated count of %d", r.URL.Path, wrap.count)
v.setMiddlewareVal(wrap.count)
v.ServeHTTP(w, r)
} else {
log.Printf("%s: valWrapper wrapping handler that is NOT counter", r.URL.Path)
wrap.Handler.ServeHTTP(w, r)
}
}
type ptrWrapper struct {
count int
Handler http.Handler
}
func (wrap *ptrWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if v, ok := wrap.Handler.(counter); ok {
log.Printf("%s: ptrWrapper wrapping handler that IS counter", r.URL.Path)
log.Printf("%s: ptrWrapper has initial count of %d", r.URL.Path, wrap.count)
wrap.count += 1
log.Printf("%s: ptrWrapper has mutated count of %d", r.URL.Path, wrap.count)
v.setMiddlewareVal(wrap.count)
v.ServeHTTP(w, r)
} else {
log.Printf("%s: ptrWrapper wrapping handler that is NOT counter", r.URL.Path)
wrap.Handler.ServeHTTP(w, r)
}
}
func main() {
root := http.DefaultServeMux
root.HandleFunc("/", shared)
root.HandleFunc("/shared", shared)
val := pageData{}
root.Handle("/enclose-value", encloseValue(val))
root.Handle("/other-enclose-value", encloseValue(val))
ptr := &pageData{}
root.Handle("/enclose-pointer", enclosePointer(ptr))
root.Handle("/other-enclose-pointer", enclosePointer(ptr))
root.Handle("/val-handler", valHandler{})
root.Handle("/val-handler-ptr", &valHandler{})
root.Handle("/ptr-handler", &ptrHandler{})
root.Handle("/fn-wrap/val-handler", fnWrapper(valHandler{}))
root.Handle("/fn-wrap/val-handler-ptr", fnWrapper(&valHandler{}))
root.Handle("/fn-wrap/ptr-handler", fnWrapper(&ptrHandler{}))
root.Handle("/val-wrap/val-handler", valWrapper{Handler: valHandler{}})
root.Handle("/val-wrap/val-handler-ptr", valWrapper{Handler: &valHandler{}})
root.Handle("/val-wrap/ptr-handler", valWrapper{Handler: &ptrHandler{}})
root.Handle("/val-wrap-ptr/val-handler", &valWrapper{Handler: valHandler{}})
root.Handle("/val-wrap-ptr/val-handler-ptr", &valWrapper{Handler: &valHandler{}})
root.Handle("/val-wrap-ptr/ptr-handler", &valWrapper{Handler: &ptrHandler{}})
root.Handle("/ptr-wrap/val-handler", &ptrWrapper{Handler: valHandler{}})
root.Handle("/ptr-wrap/val-handler-ptr", &ptrWrapper{Handler: &valHandler{}})
root.Handle("/ptr-wrap/ptr-handler", &ptrWrapper{Handler: &ptrHandler{}})
log.Println("listening at :9500")
http.ListenAndServe(":9500", root)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment