diff --git a/fe/http.go b/fe/http.go index ff0333bbfc98b6ab9e9a0432266351d9b32f2743..1159efc58148f6b736c56979da3051671ded8f84 100644 --- a/fe/http.go +++ b/fe/http.go @@ -99,10 +99,19 @@ func (h *HttpRedirector) serveSource(w http.ResponseWriter, r *http.Request) { return } - // Hijack the incoming connection. This is just so that we can - // reset the timeout on the underlying network connection - // (which we have no use for once the stream has been - // established), but then we get to run the two-way proxy... + // Hijack the incoming connection. This is necessary, rather + // than using httputil.ReverseProxy, for two important + // reasons: + // + // 1) So that we can reset the timeout on the underlying + // network connection (which we have no use for once the + // stream has been established). + // + // 2) Because streaming is still mostly a HTTP/1.0 world, the + // HTTP/1.1 features used by Go's net/http package (mostly the + // chunked encoding, I think) will apparently confuse clients + // and servers alike. + // conn, _, err := w.(http.Hijacker).Hijack() if err != nil { log.Printf("source: hijack failed: %v", err) @@ -166,50 +175,52 @@ func (h *HttpRedirector) serveStatusPage(w http.ResponseWriter, r *http.Request) w.Write(buf.Bytes()) } -func (h *HttpRedirector) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "" || r.URL.Path == "/" { - // Serve the status page through a GZIPHandler. Binds - // to h using function closure. - handler := handlers.GZIPHandler( - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - h.serveStatusPage(w, r) - }), nil) - handler.ServeHTTP(w, r) - } else if r.Method == "SOURCE" { - h.serveSource(w, r) - } else { - h.serveRelay(w, r) - } -} - // Run starts the HTTP server on the given addr. Does not return. func (h *HttpRedirector) Run(addr, staticDir, templateDir string) { h.template = template.Must( template.ParseGlob( filepath.Join(templateDir, "*.html"))) - // The purpose of the odd usage of GZIPHandler is to bypass it - // on SOURCE and m3u requests. May not be necessary though. + // Create our HTTP handler stack. Passes the /debug/ queries + // along to the global ServeMux (where moodules such as pprof + // install their handlers). mux := http.NewServeMux() - mux.HandleFunc( + mux.Handle( "/static/", - handlers.GZIPHandler( - http.StripPrefix( - "/static/", - http.FileServer(http.Dir(staticDir))), - nil)) - mux.Handle("/", h) - - // It would be nice to add a logging handler on top of - // everything, but we would become unable to hijack the - // connection on SOURCE requests... - // - //logopts := handlers.NewLogOptions(nil, handlers.Lshort) - //logger := handlers.LogHandler(mux, logopts) + http.StripPrefix( + "/static/", + http.FileServer(http.Dir(staticDir)))) + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + switch { + case r.URL.Path == "" || r.URL.Path == "/": + h.serveStatusPage(w, r) + case strings.HasPrefix(r.URL.Path, "/debug/"): + http.DefaultServeMux.ServeHTTP(w, r) + default: + h.serveRelay(w, r) + } + }) + + // Add some handlers to support gzip-encoded responses and + // request logging. + wraph := handlers.GZIPHandler(mux, nil) + logopts := handlers.NewLogOptions(nil, handlers.Lshort) + wraph = handlers.LogHandler(wraph, logopts) + + // Serve SOURCE requests bypassing the logging and gzip + // handlers: since they wrap the ResponseWriter, we would be + // unable to hijack the underlying connection for proxying. + rooth := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == "SOURCE" { + h.serveSource(w, r) + } else { + wraph.ServeHTTP(w, r) + } + }) httpServer := &http.Server{ Addr: addr, - Handler: mux, + Handler: rooth, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, }