1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
|
/*
The xSendFile middleware transparently sends static files in HTTP responses
via the X-Sendfile mechanism. All that is needed in the Rails code is the
'send_file' method.
*/
package sendfile
import (
"log"
"net/http"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
)
const sendFileResponseHeader = "X-Sendfile"
type sendFileResponseWriter struct {
rw http.ResponseWriter
status int
hijacked bool
req *http.Request
}
func SendFile(h http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
s := &sendFileResponseWriter{
rw: rw,
req: req,
}
// Advertise to upstream (Rails) that we support X-Sendfile
req.Header.Set("X-Sendfile-Type", "X-Sendfile")
defer s.Flush()
h.ServeHTTP(s, req)
})
}
func (s *sendFileResponseWriter) Header() http.Header {
return s.rw.Header()
}
func (s *sendFileResponseWriter) Write(data []byte) (n int, err error) {
if s.status == 0 {
s.WriteHeader(http.StatusOK)
}
if s.hijacked {
return
}
return s.rw.Write(data)
}
func (s *sendFileResponseWriter) WriteHeader(status int) {
if s.status != 0 {
return
}
s.status = status
if s.status != http.StatusOK {
s.rw.WriteHeader(s.status)
return
}
if file := s.Header().Get(sendFileResponseHeader); file != "" {
s.Header().Del(sendFileResponseHeader)
// Mark this connection as hijacked
s.hijacked = true
// Serve the file
sendFileFromDisk(s.rw, s.req, file)
return
}
s.rw.WriteHeader(s.status)
return
}
func sendFileFromDisk(w http.ResponseWriter, r *http.Request, file string) {
log.Printf("Send file %q for %s %q", file, r.Method, r.RequestURI)
content, fi, err := helper.OpenFile(file)
if err != nil {
http.NotFound(w, r)
return
}
defer content.Close()
http.ServeContent(w, r, "", fi.ModTime(), content)
}
func (s *sendFileResponseWriter) Flush() {
s.WriteHeader(http.StatusOK)
}
|