1
0
Fork 0
mirror of https://git.sr.ht/~adnano/go-gemini synced 2024-05-28 18:06:14 +02:00

server: Recover from ServeGemini panics

This commit is contained in:
Adnan Maolood 2021-02-15 00:36:08 -05:00
parent 96a84ddd38
commit 2c7f8273e9
2 changed files with 19 additions and 2 deletions

View File

@ -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")
) )

View File

@ -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