server: Recover from ServeGemini panics
This commit is contained in:
parent
96a84ddd38
commit
2c7f8273e9
@ -19,4 +19,10 @@ var (
|
|||||||
// ErrServerClosed is returned by the Server's Serve and ListenAndServe
|
// ErrServerClosed is returned by the Server's Serve and ListenAndServe
|
||||||
// methods after a call to Shutdown or Close.
|
// methods after a call to Shutdown or Close.
|
||||||
ErrServerClosed = errors.New("gemini: server closed")
|
ErrServerClosed = errors.New("gemini: server closed")
|
||||||
|
|
||||||
|
// ErrAbortHandler is a sentinel panic value to abort a handler.
|
||||||
|
// While any panic from ServeGemini aborts the response to the client,
|
||||||
|
// panicking with ErrAbortHandler also suppresses logging of a stack
|
||||||
|
// trace to the server's error log.
|
||||||
|
ErrAbortHandler = errors.New("net/http: abort Handler")
|
||||||
)
|
)
|
||||||
|
15
server.go
15
server.go
@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@ -348,6 +349,15 @@ func (srv *Server) deleteConn(conn *net.Conn) {
|
|||||||
func (srv *Server) respond(conn net.Conn) {
|
func (srv *Server) respond(conn net.Conn) {
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil && err != ErrAbortHandler {
|
||||||
|
const size = 64 << 10
|
||||||
|
buf := make([]byte, size)
|
||||||
|
buf = buf[:runtime.Stack(buf, false)]
|
||||||
|
srv.logf("gemini: panic serving %v: %v\n%s", conn.RemoteAddr(), err, buf)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
srv.trackConn(&conn)
|
srv.trackConn(&conn)
|
||||||
defer srv.deleteConn(&conn)
|
defer srv.deleteConn(&conn)
|
||||||
|
|
||||||
@ -359,11 +369,11 @@ func (srv *Server) respond(conn net.Conn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
w := NewResponseWriter(conn)
|
w := NewResponseWriter(conn)
|
||||||
defer w.Flush()
|
|
||||||
|
|
||||||
req, err := ReadRequest(conn)
|
req, err := ReadRequest(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.Status(StatusBadRequest)
|
w.Status(StatusBadRequest)
|
||||||
|
w.Flush()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,10 +389,12 @@ func (srv *Server) respond(conn net.Conn) {
|
|||||||
resp := srv.responder(req)
|
resp := srv.responder(req)
|
||||||
if resp == nil {
|
if resp == nil {
|
||||||
w.Status(StatusNotFound)
|
w.Status(StatusNotFound)
|
||||||
|
w.Flush()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp.ServeGemini(w, req)
|
resp.ServeGemini(w, req)
|
||||||
|
w.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) responder(r *Request) Handler {
|
func (srv *Server) responder(r *Request) Handler {
|
||||||
@ -418,7 +430,6 @@ func (srv *Server) logf(format string, args ...interface{}) {
|
|||||||
//
|
//
|
||||||
// Handlers should not modify the provided Request.
|
// Handlers should not modify the provided Request.
|
||||||
//
|
//
|
||||||
// TODO:
|
|
||||||
// If ServeGemini panics, the server (the caller of ServeGemini) assumes that
|
// If ServeGemini panics, the server (the caller of ServeGemini) assumes that
|
||||||
// the effect of the panic was isolated to the active request. It recovers
|
// the effect of the panic was isolated to the active request. It recovers
|
||||||
// the panic, logs a stack trace to the server error log, and closes the
|
// the panic, logs a stack trace to the server error log, and closes the
|
||||||
|
Loading…
Reference in New Issue
Block a user