fix(handler/file): fix deadlock, conn leak, and nil Addr; add 39 tests
Fix bugs in the file handler: unbuffered send channel deadlock on Close, conn leak when send drops on done, and Addr() returning nil. Add done- priority select in send(), explicit ln.Close() in Close(), Unwrap() on responseWriter for http.ResponseController, and nil guards. Also add package doc, NewHandler doc, and 39 unit tests covering the listener, responseWriter, handler lifecycle, auth, and file serving.
This commit is contained in:
+37
-6
@@ -1,3 +1,5 @@
|
||||
// Package file provides a static file-serving handler for GOST.
|
||||
// It serves files from a configured directory over HTTP via the GOST proxy chain.
|
||||
package file
|
||||
|
||||
import (
|
||||
@@ -31,6 +33,9 @@ type fileHandler struct {
|
||||
recorder recorder.RecorderObject
|
||||
}
|
||||
|
||||
// NewHandler creates a static file-serving handler. It registers as "file" in the
|
||||
// handler registry and serves files from the directory specified by the "file.dir"
|
||||
// or "dir" metadata key.
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
options := handler.Options{}
|
||||
for _, opt := range opts {
|
||||
@@ -60,8 +65,9 @@ func (h *fileHandler) Init(md md.Metadata) (err error) {
|
||||
}
|
||||
|
||||
h.ln = &singleConnListener{
|
||||
conn: make(chan net.Conn),
|
||||
conn: make(chan net.Conn, 1),
|
||||
done: make(chan struct{}),
|
||||
addr: &listenerAddr{},
|
||||
}
|
||||
go h.server.Serve(h.ln)
|
||||
|
||||
@@ -74,11 +80,14 @@ func (h *fileHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler
|
||||
clientAddr = srcAddr.String()
|
||||
}
|
||||
|
||||
remoteAddr := conn.RemoteAddr()
|
||||
localAddr := conn.LocalAddr()
|
||||
|
||||
h.options.Logger.WithFields(map[string]any{
|
||||
"client": clientAddr,
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
}).Infof("%s - %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
"remote": remoteAddr.String(),
|
||||
"local": localAddr.String(),
|
||||
}).Infof("%s - %s", remoteAddr, localAddr)
|
||||
|
||||
h.ln.send(conn)
|
||||
|
||||
@@ -86,6 +95,9 @@ func (h *fileHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler
|
||||
}
|
||||
|
||||
func (h *fileHandler) Close() error {
|
||||
if h.ln != nil {
|
||||
h.ln.Close()
|
||||
}
|
||||
return h.server.Close()
|
||||
}
|
||||
|
||||
@@ -149,9 +161,16 @@ func (h *fileHandler) handleFunc(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
h.handler.ServeHTTP(rw, r)
|
||||
if h.handler != nil {
|
||||
h.handler.ServeHTTP(rw, r)
|
||||
}
|
||||
}
|
||||
|
||||
type listenerAddr struct{}
|
||||
|
||||
func (a *listenerAddr) Network() string { return "file" }
|
||||
func (a *listenerAddr) String() string { return "file" }
|
||||
|
||||
type singleConnListener struct {
|
||||
conn chan net.Conn
|
||||
addr net.Addr
|
||||
@@ -187,10 +206,16 @@ func (l *singleConnListener) Addr() net.Addr {
|
||||
}
|
||||
|
||||
func (l *singleConnListener) send(conn net.Conn) {
|
||||
select {
|
||||
case <-l.done:
|
||||
conn.Close()
|
||||
return
|
||||
default:
|
||||
}
|
||||
select {
|
||||
case l.conn <- conn:
|
||||
case <-l.done:
|
||||
return
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,3 +235,9 @@ func (w *responseWriter) WriteHeader(statusCode int) {
|
||||
w.statusCode = statusCode
|
||||
w.ResponseWriter.WriteHeader(statusCode)
|
||||
}
|
||||
|
||||
// Unwrap returns the underlying ResponseWriter, allowing http.ResponseController
|
||||
// to discover optional interfaces (Flusher, Hijacker, etc.) on the wrapped writer.
|
||||
func (w *responseWriter) Unwrap() http.ResponseWriter {
|
||||
return w.ResponseWriter
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user