func (th *teeHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { t0 := time.Now() if *mimeType != "" { w.Header().Set("Content-Type", *mimeType) } else if *mp3only { w.Header().Set("Content-Type", "audio/mpeg") } cw := &countingWriter{Writer: w} sc := &signalCloser{Writer: cw, Closed: make(chan struct{})} log.Printf("%d +%+q", atomic.AddInt64(&th.clients, 1), req.RemoteAddr) th.Add(sc) errs := make(chan error) go func() { if w, ok := w.(http.CloseNotifier); ok { <-w.CloseNotify() } else { <-sc.Closed } errs <- th.RemoveAndClose(sc) }() err := <-errs errStr := "" if err != nil { errStr = err.Error() } t := time.Since(t0) log.Printf("%d -%+q %s %d =%dB/s %+q", atomic.AddInt64(&th.clients, -1), req.RemoteAddr, t, cw.Count(), int64(float64(cw.Count())/t.Seconds()), errStr) }
// Get a buffer from the pool -- but give up and return a non-nil // error if resp implements http.CloseNotifier and tells us that the // client has disconnected before we get a buffer. func getBufferForResponseWriter(resp http.ResponseWriter, bufs *bufferPool, bufSize int) ([]byte, error) { var closeNotifier <-chan bool if resp, ok := resp.(http.CloseNotifier); ok { closeNotifier = resp.CloseNotify() } var buf []byte bufReady := make(chan []byte) go func() { bufReady <- bufs.Get(bufSize) close(bufReady) }() select { case buf = <-bufReady: return buf, nil case <-closeNotifier: go func() { // Even if closeNotifier happened first, we // need to keep waiting for our buf so we can // return it to the pool. bufs.Put(<-bufReady) }() return nil, ErrClientDisconnect } }