I'm working with golang web server and handlers and ran into a little problem. I want to serve one file with the following text. The problem is that the messages are not displayed. Here is the handler code:
mux := http.NewServeMux()
mux.HandleFunc("/myfile", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "./ui/static/js/main.js")
w.Write([]byte("Message after (1)"))
w.Write([]byte("Message after (2)"))
fmt.Println("Console message!")
})
http.ListenAndServe(":4000", mux)
The output in the browser is:
var navLinks = document.querySelectorAll("nav a");
for (var i = 0; i < navLinks.length; i ) {
var link = navLinks[i]
if (link.getAttribute('href') == window.location.pathname) {
link.classList.add("live");
break;
}
}
But when I write one message (or more) before the file, it shows both the messages after and before the file:
mux := http.NewServeMux()
mux.HandleFunc("/myfile", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Message 1 (before)"))
http.ServeFile(w, r, "./ui/static/js/main.js")
w.Write([]byte("Message 1 (after)"))
w.Write([]byte("Message 2 (after)"))
fmt.Println("Console message!")
})
http.ListenAndServe(":4000", mux)
The output in the browser is:
Message 1 (before)var navLinks = document.querySelectorAll("nav a");
for (var i = 0; i < navLinks.length; i ) {
var link = navLinks[i]
if (link.getAttribute('href') == window.location.pathname) {
link.classList.add("live");
break;
}
}Message 1 (after)Message 2 (after)
Does anyone know why this happens and how to fix it? In other words, why are messages after the file not displayed when there is no message before the file?
fmt.Println("Console message!")
is always displayed in the console.
CodePudding user response:
The net/http server does one of thee things with regards to framing the response body:
- If the application sets the response content length header before the first call to write, then the server uses identity encoding with the application specified content length header.
- If the response body fits in fixed size buffer (currently 2048 bytes), the server sets the content length header to the actual content length and uses identity encoding.
- Otherwise, the server uses chunked encoding with no content length response header.
First example:
The call to http.ServeFile sets the response content length header to the size of the file before the first call to Write. The client reads the number of bytes specified by the content length header.
The content length does not include the size of two messages. These messages are ignored by the client or the two messages cause an error the next HTTP transaction on the network connection.
Second example (call to Write before ServeFile):
Modifications to the response header are ignored after the first call to Write. The ServeFile function sets the response content length header to the size of the file, but that value is ignored.
The client reads the all of the messages and file because the server either used chunked encoding or set the content length header to the actual size of the file.
Some ServeFile features may work correctly because ServeFile cannot set the response headers or response status code.
Fix:
Open the file in your handler and copy to the response. You cannot use http.ServeFile.
CodePudding user response:
http.ServeFile
tries to set the Content-Length
header (along with a bunch of other headers that are good for serving static files, like Last-Modified
). Since it sets Content-Length
to the length of the file, the browser quits listening to you after the last byte of the file. If you do w.Write([]byte("Message 1 (before)"))
this jumps ahead to the response body part and makes it impossible for http.ServeFile
to set any headers.