diff options
| author | Adnan Maolood <[email protected]> | 2021-02-23 22:12:04 -0500 |
|---|---|---|
| committer | Adnan Maolood <[email protected]> | 2021-02-23 22:12:04 -0500 |
| commit | f08efa330f7fbb8c7d0866f091fde7ace8ad1347 (patch) | |
| tree | 6ee8e3ffe82ab91cf5c5d26f2793b088be03b3eb /handler.go | |
| parent | Unexport NewResponseWriter (diff) | |
| download | go-gemini-f08efa330f7fbb8c7d0866f091fde7ace8ad1347.tar.xz go-gemini-f08efa330f7fbb8c7d0866f091fde7ace8ad1347.zip | |
Move TimeoutHandler to handler.go
Diffstat (limited to 'handler.go')
| -rw-r--r-- | handler.go | 44 |
1 files changed, 44 insertions, 0 deletions
@@ -1,9 +1,11 @@ package gemini import ( + "bytes" "context" "net/url" "strings" + "time" ) // A Handler responds to a Gemini request. @@ -72,3 +74,45 @@ func StripPrefix(prefix string, h Handler) Handler { } }) } + +// TimeoutHandler returns a Handler that runs h with the given time limit. +// +// The new Handler calls h.ServeGemini to handle each request, but +// if a call runs for longer than its time limit, the handler responds with a +// 40 Temporary Failure error. After such a timeout, writes by h to its +// ResponseWriter will return ErrHandlerTimeout. +func TimeoutHandler(h Handler, dt time.Duration) Handler { + return &timeoutHandler{ + h: h, + dt: dt, + } +} + +type timeoutHandler struct { + h Handler + dt time.Duration +} + +func (t *timeoutHandler) ServeGemini(ctx context.Context, w *ResponseWriter, r *Request) { + ctx, cancel := context.WithTimeout(ctx, t.dt) + defer cancel() + + conn := w.Hijack() + + var b bytes.Buffer + w.reset(nopCloser{&b}) + + done := make(chan struct{}) + go func() { + t.h.ServeGemini(ctx, w, r) + close(done) + }() + + select { + case <-done: + conn.Write(b.Bytes()) + case <-ctx.Done(): + w.reset(conn) + w.WriteHeader(StatusTemporaryFailure, "Timeout") + } +} |