1
0
mirror of https://git.sr.ht/~adnano/go-gemini synced 2024-09-28 15:21:23 +02:00

Add ResponseWriter.Hijack method

This commit is contained in:
Adnan Maolood 2021-02-23 21:36:29 -05:00
parent d35dd3d867
commit f28a63ff0c
2 changed files with 36 additions and 0 deletions

@ -20,4 +20,11 @@ var (
// ErrHandlerTimeout is returned on ResponseWriter Write calls
// in handlers which have timed out.
ErrHandlerTimeout = errors.New("gemini: Handler timeout")
// ErrHijacked is returned by ResponseWriter.Write calls when
// the underlying connection has been hijacked using the
// Hijacker interface. A zero-byte write on a hijacked
// connection will return ErrHijacked without any other side
// effects.
ErrHijacked = errors.New("gemini: connection has been hijacked")
)

@ -126,6 +126,7 @@ type ResponseWriter struct {
mediatype string
wroteHeader bool
bodyAllowed bool
hijacked bool
conn net.Conn
}
@ -154,6 +155,9 @@ func (w *ResponseWriter) SetMediaType(mediatype string) {
// If no media type was set, Write uses a default media type of
// "text/gemini; charset=utf-8".
func (w *ResponseWriter) Write(b []byte) (int, error) {
if w.hijacked {
return 0, ErrHijacked
}
if !w.wroteHeader {
meta := w.mediatype
if meta == "" {
@ -179,6 +183,9 @@ func (w *ResponseWriter) Write(b []byte) (int, error) {
// The provided meta must not be longer than 1024 bytes.
// Only one header may be written.
func (w *ResponseWriter) WriteHeader(status Status, meta string) {
if w.hijacked {
return
}
if w.wroteHeader {
return
}
@ -196,6 +203,9 @@ func (w *ResponseWriter) WriteHeader(status Status, meta string) {
// Flush sends any buffered data to the client.
func (w *ResponseWriter) Flush() error {
if w.hijacked {
return ErrHijacked
}
if !w.wroteHeader {
w.WriteHeader(StatusTemporaryFailure, "Temporary failure")
}
@ -206,10 +216,14 @@ func (w *ResponseWriter) Flush() error {
// Close closes the connection.
// Any blocked Write operations will be unblocked and return errors.
func (w *ResponseWriter) Close() error {
if w.hijacked {
return ErrHijacked
}
return w.closer.Close()
}
// Conn returns the underlying network connection.
// To take over the connection, use Hijack.
func (w *ResponseWriter) Conn() net.Conn {
return w.conn
}
@ -222,3 +236,18 @@ func (w *ResponseWriter) TLS() *tls.ConnectionState {
}
return nil
}
// Hijack lets the caller take over the connection.
// After a call to Hijack the Gemini server library
// will not do anything else with the connection.
// It becomes the caller's responsibility to manage
// and close the connection.
//
// The returned net.Conn may have read or write deadlines
// already set, depending on the configuration of the
// Server. It is the caller's responsibility to set
// or clear those deadlines as needed.
func (w *ResponseWriter) Hijack() net.Conn {
w.hijacked = true
return w.conn
}