Last active
November 29, 2023 11:41
-
-
Save justinas/7059324 to your computer and use it in GitHub Desktop.
Go middleware samples for my blog post. http://justinas.org/writing-http-middleware-in-go/
This file contains 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 ( | |
"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) | |
} |
This file contains 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 ( | |
"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) | |
} |
This file contains 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 ( | |
"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) | |
} |
This file contains 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 ( | |
"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) | |
} |
@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!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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!