From f6824bd813e1e9fbc78bb2b0a167926de6dab7ed Mon Sep 17 00:00:00 2001 From: Adnan Maolood Date: Tue, 9 Feb 2021 09:45:10 -0500 Subject: [PATCH] Make ResponseWriter an interface --- examples/auth.go | 4 +-- examples/stream.go | 2 +- fs.go | 4 +-- mux.go | 4 +-- response.go | 69 +++++++++++++++++++++++++++------------------- server.go | 8 +++--- 6 files changed, 52 insertions(+), 39 deletions(-) diff --git a/examples/auth.go b/examples/auth.go index d9325ee..34d336a 100644 --- a/examples/auth.go +++ b/examples/auth.go @@ -54,7 +54,7 @@ func fingerprint(cert *x509.Certificate) string { return string(b[:]) } -func profile(w *gemini.ResponseWriter, r *gemini.Request) { +func profile(w gemini.ResponseWriter, r *gemini.Request) { if r.Certificate == nil { w.Status(gemini.StatusCertificateRequired) return @@ -69,7 +69,7 @@ func profile(w *gemini.ResponseWriter, r *gemini.Request) { fmt.Fprintln(w, "=> /username Change username") } -func changeUsername(w *gemini.ResponseWriter, r *gemini.Request) { +func changeUsername(w gemini.ResponseWriter, r *gemini.Request) { if r.Certificate == nil { w.Status(gemini.StatusCertificateRequired) return diff --git a/examples/stream.go b/examples/stream.go index 1a292fd..0c302bd 100644 --- a/examples/stream.go +++ b/examples/stream.go @@ -38,7 +38,7 @@ func main() { } // stream writes an infinite stream to w. -func stream(w *gemini.ResponseWriter, r *gemini.Request) { +func stream(w gemini.ResponseWriter, r *gemini.Request) { ch := make(chan string) ctx, cancel := context.WithCancel(context.Background()) diff --git a/fs.go b/fs.go index ced8bd6..56d1432 100644 --- a/fs.go +++ b/fs.go @@ -26,7 +26,7 @@ type fsHandler struct { FS } -func (fsh fsHandler) ServeGemini(w *ResponseWriter, r *Request) { +func (fsh fsHandler) ServeGemini(w ResponseWriter, r *Request) { p := path.Clean(r.URL.Path) f, err := fsh.Open(p) if err != nil { @@ -73,7 +73,7 @@ func (d Dir) Open(name string) (File, error) { // or directory. // // TODO: Use io/fs.FS when available. -func ServeFile(w *ResponseWriter, fs FS, name string) { +func ServeFile(w ResponseWriter, fs FS, name string) { f, err := fs.Open(name) if err != nil { w.Status(StatusNotFound) diff --git a/mux.go b/mux.go index c2a8d28..7fd4113 100644 --- a/mux.go +++ b/mux.go @@ -132,7 +132,7 @@ func (mux *ServeMux) shouldRedirectRLocked(path string) bool { // ServeGemini dispatches the request to the handler whose // pattern most closely matches the request URL. -func (mux *ServeMux) ServeGemini(w *ResponseWriter, r *Request) { +func (mux *ServeMux) ServeGemini(w ResponseWriter, r *Request) { path := cleanPath(r.URL.Path) // If the given path is /tree and its handler is not registered, @@ -202,7 +202,7 @@ func appendSorted(es []muxEntry, e muxEntry) []muxEntry { } // HandleFunc registers the handler function for the given pattern. -func (mux *ServeMux) HandleFunc(pattern string, handler func(*ResponseWriter, *Request)) { +func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { if handler == nil { panic("gemini: nil responder") } diff --git a/response.go b/response.go index 686c7b8..4b71855 100644 --- a/response.go +++ b/response.go @@ -129,8 +129,39 @@ func (b *readCloserBody) Read(p []byte) (n int, err error) { return b.ReadCloser.Read(p) } -// ResponseWriter is used to construct a Gemini response. -type ResponseWriter struct { +// A ResponseWriter interface is used by a Gemini handler +// to construct a Gemini response. +type ResponseWriter interface { + // Header sets the response header. + Header(status int, meta string) + + // Status sets the response status code. + // It also sets the response meta to Meta(status). + Status(status int) + + // Meta sets the response meta. + // + // For successful responses, meta should contain the media type of the response. + // For failure responses, meta should contain a short description of the failure. + // The response meta should not be greater than 1024 bytes. + Meta(meta string) + + // Write writes data to the connection as part of the response body. + // If the response status does not allow for a response body, Write returns + // ErrBodyNotAllowed. + // + // Write writes the response header if it has not already been written. + // It writes a successful status code if one is not set. + Write([]byte) (int, error) + + // Flush writes any buffered data to the underlying io.Writer. + // + // Flush writes the response header if it has not already been written. + // It writes a failure status code if one is not set. + Flush() error +} + +type responseWriter struct { b *bufio.Writer status int meta string @@ -139,41 +170,27 @@ type ResponseWriter struct { } // NewResponseWriter returns a ResponseWriter that uses the provided io.Writer. -func NewResponseWriter(w io.Writer) *ResponseWriter { - return &ResponseWriter{ +func NewResponseWriter(w io.Writer) ResponseWriter { + return &responseWriter{ b: bufio.NewWriter(w), } } -// Header sets the response header. -func (w *ResponseWriter) Header(status int, meta string) { +func (w *responseWriter) Header(status int, meta string) { w.status = status w.meta = meta } -// Status sets the response status code. -// It also sets the response meta to Meta(status). -func (w *ResponseWriter) Status(status int) { +func (w *responseWriter) Status(status int) { w.status = status w.meta = Meta(status) } -// Meta sets the response meta. -// -// For successful responses, meta should contain the media type of the response. -// For failure responses, meta should contain a short description of the failure. -// The response meta should not be greater than 1024 bytes. -func (w *ResponseWriter) Meta(meta string) { +func (w *responseWriter) Meta(meta string) { w.meta = meta } -// Write writes data to the connection as part of the response body. -// If the response status does not allow for a response body, Write returns -// ErrBodyNotAllowed. -// -// Write writes the response header if it has not already been written. -// It writes a successful status code if one is not set. -func (w *ResponseWriter) Write(b []byte) (int, error) { +func (w *responseWriter) Write(b []byte) (int, error) { if !w.wroteHeader { w.writeHeader(StatusSuccess) } @@ -183,7 +200,7 @@ func (w *ResponseWriter) Write(b []byte) (int, error) { return w.b.Write(b) } -func (w *ResponseWriter) writeHeader(defaultStatus int) { +func (w *responseWriter) writeHeader(defaultStatus int) { status := w.status if status == 0 { status = defaultStatus @@ -205,11 +222,7 @@ func (w *ResponseWriter) writeHeader(defaultStatus int) { w.wroteHeader = true } -// Flush writes any buffered data to the underlying io.Writer. -// -// Flush writes the response header if it has not already been written. -// It writes a failure status code if one is not set. -func (w *ResponseWriter) Flush() error { +func (w *responseWriter) Flush() error { if !w.wroteHeader { w.writeHeader(StatusTemporaryFailure) } diff --git a/server.go b/server.go index a72a840..095437e 100644 --- a/server.go +++ b/server.go @@ -83,7 +83,7 @@ func (s *Server) Handle(pattern string, handler Handler) { } // HandleFunc registers the handler function for the given pattern. -func (s *Server) HandleFunc(pattern string, handler func(*ResponseWriter, *Request)) { +func (s *Server) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { s.Handle(pattern, HandlerFunc(handler)) } @@ -258,15 +258,15 @@ func (s *Server) logf(format string, args ...interface{}) { // ServeGemini should write the response header and data to the ResponseWriter // and then return. type Handler interface { - ServeGemini(*ResponseWriter, *Request) + ServeGemini(ResponseWriter, *Request) } // The HandlerFunc type is an adapter to allow the use of ordinary functions // as Gemini handlers. If f is a function with the appropriate signature, // HandlerFunc(f) is a Handler that calls f. -type HandlerFunc func(*ResponseWriter, *Request) +type HandlerFunc func(ResponseWriter, *Request) // ServeGemini calls f(w, r). -func (f HandlerFunc) ServeGemini(w *ResponseWriter, r *Request) { +func (f HandlerFunc) ServeGemini(w ResponseWriter, r *Request) { f(w, r) }