-
-
Save jordanorelli/45d5b0586aa264fc31de6932bf28e832 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 /> | |
{{if .MiddlewareCount }} | |
Middleware Count: {{ .MiddlewareCount }} <br /> | |
{{end}} | |
<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