2021-02-19 22:49:21 -07:00
|
|
|
package gemini
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2021-02-23 19:59:16 -07:00
|
|
|
func (t *timeoutHandler) ServeGemini(ctx context.Context, w *ResponseWriter, r *Request) {
|
2021-02-20 13:52:54 -07:00
|
|
|
ctx, cancel := context.WithTimeout(ctx, t.dt)
|
2021-02-20 11:37:08 -07:00
|
|
|
defer cancel()
|
2021-02-19 22:49:21 -07:00
|
|
|
|
2021-02-23 19:59:16 -07:00
|
|
|
conn := w.Hijack()
|
|
|
|
|
|
|
|
var b bytes.Buffer
|
|
|
|
w.reset(nopCloser{&b})
|
|
|
|
|
2021-02-19 22:49:21 -07:00
|
|
|
done := make(chan struct{})
|
|
|
|
go func() {
|
2021-02-23 19:59:16 -07:00
|
|
|
t.h.ServeGemini(ctx, w, r)
|
2021-02-19 22:49:21 -07:00
|
|
|
close(done)
|
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-done:
|
2021-02-23 19:59:16 -07:00
|
|
|
conn.Write(b.Bytes())
|
2021-02-19 22:49:21 -07:00
|
|
|
case <-ctx.Done():
|
2021-02-23 19:59:16 -07:00
|
|
|
w.reset(conn)
|
2021-02-19 22:49:21 -07:00
|
|
|
w.WriteHeader(StatusTemporaryFailure, "Timeout")
|
|
|
|
}
|
2021-02-23 15:32:23 -07:00
|
|
|
}
|