-
-
Save justinas/7059324 to your computer and use it in GitHub Desktop.
package main | |
import ( | |
"net/http" | |
) | |
type SingleHost struct { | |
handler http.Handler | |
allowedHost string | |
} | |
func NewSingleHost(handler http.Handler, allowedHost string) *SingleHost { | |
return &SingleHost{handler: handler, allowedHost: allowedHost} | |
} | |
func (s *SingleHost) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
host := r.Host | |
if host == s.allowedHost { | |
s.handler.ServeHTTP(w, r) | |
} else { | |
w.WriteHeader(403) | |
} | |
} | |
func myHandler(w http.ResponseWriter, r *http.Request) { | |
w.Write([]byte("Success!")) | |
} | |
func main() { | |
single := NewSingleHost(http.HandlerFunc(myHandler), "example.com") | |
println("Listening on port 8080") | |
http.ListenAndServe(":8080", single) | |
} |
package main | |
import ( | |
"net/http" | |
) | |
func SingleHost(handler http.Handler, allowedHost string) http.Handler { | |
ourFunc := func(w http.ResponseWriter, r *http.Request) { | |
host := r.Host | |
if host == allowedHost { | |
handler.ServeHTTP(w, r) | |
} else { | |
w.WriteHeader(403) | |
} | |
} | |
return http.HandlerFunc(ourFunc) | |
} | |
func myHandler(w http.ResponseWriter, r *http.Request) { | |
w.Write([]byte("Success!")) | |
} | |
func main() { | |
single := SingleHost(http.HandlerFunc(myHandler), "example.com") | |
println("Listening on port 8080") | |
http.ListenAndServe(":8080", single) | |
} |
package main | |
import ( | |
"net/http" | |
) | |
type AppendMiddleware struct { | |
handler http.Handler | |
} | |
func (a *AppendMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
a.handler.ServeHTTP(w, r) | |
w.Write([]byte("<!-- Middleware says hello! -->")) | |
} | |
func myHandler(w http.ResponseWriter, r *http.Request) { | |
w.Write([]byte("Success!")) | |
} | |
func main() { | |
mid := &AppendMiddleware{http.HandlerFunc(myHandler)} | |
println("Listening on port 8080") | |
http.ListenAndServe(":8080", mid) | |
} |
package main | |
import ( | |
"net/http" | |
"net/http/httptest" | |
) | |
type ModifierMiddleware struct { | |
handler http.Handler | |
} | |
func (m *ModifierMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
rec := httptest.NewRecorder() | |
// passing a ResponseRecorder instead of the original RW | |
m.handler.ServeHTTP(rec, r) | |
// after this finishes, we have the response recorded | |
// and can modify it before copying it to the original RW | |
// we copy the original headers first | |
for k, v := range rec.Header() { | |
w.Header()[k] = v | |
} | |
// and set an additional one | |
w.Header().Set("X-We-Modified-This", "Yup") | |
// only then the status code, as this call writes the headers as well | |
w.WriteHeader(418) | |
// The body hasn't been written (to the real RW) yet, | |
// so we can prepend some data. | |
data := []byte("Middleware says hello again. ") | |
// But the Content-Length might have been set already, | |
// we should modify it by adding the length | |
// of our own data. | |
// Ignoring the error is fine here: | |
// if Content-Length is empty or otherwise invalid, | |
// Atoi() will return zero, | |
// which is just what we'd want in that case. | |
clen, _ := strconv.Atoi(r.Header.Get("Content-Length")) | |
clen += len(data) | |
w.Header.Set("Content-Length", strconv.Itoa(clen)) | |
// finally, write out our data | |
w.Write(data) | |
// then write out the original body | |
w.Write(rec.Body.Bytes()) | |
} | |
func myHandler(w http.ResponseWriter, r *http.Request) { | |
w.Write([]byte("Success!")) | |
} | |
func main() { | |
mid := &ModifierMiddleware{http.HandlerFunc(myHandler)} | |
println("Listening on port 8080") | |
http.ListenAndServe(":8080", mid) | |
} |
Hi justinas !
Great job.
Noob question. Do you see any chance to make alice and https://github.com/julienschmidt/httprouter working together without rewriting code? Issue I see, httprouter has additional in handler.
Thank you in advance
There are two problems with 4_modifier.go.
- You should not ignore the error return values of the calls to
w.Write
. Ignoring them makes it much harder to track down the issues caused by problem #2. - If the Handler that you are wrapping sets a Content-Length header and you write that header as-is to your new ResponseWriter and you make a change to the response's content that increases its length then Write will fail and return an error. If you do not check for that error (see #1 above) and just silently proceed you'll be left to scratch your head, wondering where you content went. See bradfitz's explanation in this thread in the golang-nuts forum.
@hartzell, thanks so much. I think @pengfei-xue referred to the same issue, though I had trouble understanding it.
I have updated the gist (and the blog post) for #2, though I am not sure about #1. It would make sense to add it everywhere, instead of just that one example, as weird stuff might happen anywhere and in theory, every Write()
could fail.
What would be a good way for your modifier middleware to alter the HTTP Status Code of the response? I found some old discussion on the GoNuts list from 30 months ago but none of what they suggested seems to work anymore!
DOH! Nevermind. ResponseRecorder has it. I should have looked closer. Silly Me!
@justinas,
I think, that you should use w
instead of r
while updating Content-Length (and read it from rec
). I'm right?
@Komosa, yes, thank you!
Very good resources! I just made one lecture based on your blog about middleware, but that's in Chinese in case you might be interested in https://github.com/Unknwon/go-web-foundation/blob/master/lectures/lecture10/lecture10.md