I am trying to create a very simple gzip middleware for static files. But I am calling next.ServeHTTP(w, r)
5 different places in the code, what happens if I defer this? Will this be called before the function that is returned is run?
This is what I have:
func gzipHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
// If for some weird reason client does not understand gzip, then continue.
next.ServeHTTP(w, r)
return
}
path := filepath.FromSlash(filepath.Join(cfg.PublicHTML, r.URL.Path))
if _, err := os.Stat(path); os.IsNotExist(err) {
// If file or folder does not exists, then continue.
next.ServeHTTP(w, r)
return
}
var ext string
for _, v := range cfg.GzipExt {
if strings.HasSuffix(r.URL.Path, v) {
ext = v
}
}
if ext == "" {
// This file should not be served as gzipped content.
next.ServeHTTP(w, r)
return
}
// Only serve gzipped file if it exists.
if _, err := os.Stat(path + ".gz"); os.IsNotExist(err) {
// TODO: Create the gzipped file.
// http://stackoverflow.com/questions/16890648/how-can-i-use-golangs-compress-gzip-package-to-gzip-a-file
next.ServeHTTP(w, r)
return
}
w.Header().Add("Content-Encoding", "gzip")
r.URL.Path = r.URL.Path + ".gz"
next.ServeHTTP(w, r)
})
}
Would it be possible here to defer next.ServeHTTP(w, r)? Like this:
func gzipHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer next.ServeHTTP(w, r)
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
// If for some weird reason client does not understand gzip, then continue.
return
}
path := filepath.FromSlash(filepath.Join(cfg.PublicHTML, r.URL.Path))
if _, err := os.Stat(path); os.IsNotExist(err) {
// If file or folder does not exists, then continue.
return
}
var ext string
for _, v := range cfg.GzipExt {
if strings.HasSuffix(r.URL.Path, v) {
ext = v
}
}
if ext == "" {
// This file should not be served as gzipped content.
return
}
// Only serve gzipped file if it exists.
if _, err := os.Stat(path + ".gz"); os.IsNotExist(err) {
// TODO: Create the gzipped file.
// http://stackoverflow.com/questions/16890648/how-can-i-use-golangs-compress-gzip-package-to-gzip-a-file
return
}
w.Header().Add("Content-Encoding", "gzip")
r.URL.Path = r.URL.Path + ".gz"
})
}
I am using this in my main() function like this to serve static files:
router.NotFound = gzipHandler(fileServer())
If I defer next.ServeHTTP(w, r) like this, will it be executed before fileServer() is executed?
golang spec
In a function call, the function value and arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution.
gzipHandler(fileServer())
is somewhat like this:
a:=fileServer()
gzipHandler(a)
So, obviously fileServer()
is executed first.
but i think what confuse you is when will defer
statement execute, right?
according to the spec
Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked. Instead, deferred functions are invoked immediately before the surrounding function returns, in the reverse order they were deferred. If a deferred function value evaluates to nil, execution panics when the function is invoked, not when the "defer" statement is executed.
an example to explain:
func t() {
i := 1
defer fmt.Println("first defer:", i)
defer func() {
fmt.Println("second defer:", i)
}()
i = 2
fmt.Println("t return")
}
t()
will print:
t return
second defer: 2
first defer: 1
in your code, the deferred function "next.ServeHTTP
" are invoked before anonymous function func(w http.ResponseWriter, r *http.Request)
returns.