Make ResponseWriter an interface

This commit is contained in:
Adnan Maolood 2021-02-09 09:45:10 -05:00
parent 5ef5824d6f
commit f6824bd813
6 changed files with 52 additions and 39 deletions

View File

@ -54,7 +54,7 @@ func fingerprint(cert *x509.Certificate) string {
return string(b[:]) return string(b[:])
} }
func profile(w *gemini.ResponseWriter, r *gemini.Request) { func profile(w gemini.ResponseWriter, r *gemini.Request) {
if r.Certificate == nil { if r.Certificate == nil {
w.Status(gemini.StatusCertificateRequired) w.Status(gemini.StatusCertificateRequired)
return return
@ -69,7 +69,7 @@ func profile(w *gemini.ResponseWriter, r *gemini.Request) {
fmt.Fprintln(w, "=> /username Change username") 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 { if r.Certificate == nil {
w.Status(gemini.StatusCertificateRequired) w.Status(gemini.StatusCertificateRequired)
return return

View File

@ -38,7 +38,7 @@ func main() {
} }
// stream writes an infinite stream to w. // 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) ch := make(chan string)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())

4
fs.go
View File

@ -26,7 +26,7 @@ type fsHandler struct {
FS FS
} }
func (fsh fsHandler) ServeGemini(w *ResponseWriter, r *Request) { func (fsh fsHandler) ServeGemini(w ResponseWriter, r *Request) {
p := path.Clean(r.URL.Path) p := path.Clean(r.URL.Path)
f, err := fsh.Open(p) f, err := fsh.Open(p)
if err != nil { if err != nil {
@ -73,7 +73,7 @@ func (d Dir) Open(name string) (File, error) {
// or directory. // or directory.
// //
// TODO: Use io/fs.FS when available. // 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) f, err := fs.Open(name)
if err != nil { if err != nil {
w.Status(StatusNotFound) w.Status(StatusNotFound)

4
mux.go
View File

@ -132,7 +132,7 @@ func (mux *ServeMux) shouldRedirectRLocked(path string) bool {
// ServeGemini dispatches the request to the handler whose // ServeGemini dispatches the request to the handler whose
// pattern most closely matches the request URL. // 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) path := cleanPath(r.URL.Path)
// If the given path is /tree and its handler is not registered, // 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. // 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 { if handler == nil {
panic("gemini: nil responder") panic("gemini: nil responder")
} }

View File

@ -129,8 +129,39 @@ func (b *readCloserBody) Read(p []byte) (n int, err error) {
return b.ReadCloser.Read(p) return b.ReadCloser.Read(p)
} }
// ResponseWriter is used to construct a Gemini response. // A ResponseWriter interface is used by a Gemini handler
type ResponseWriter struct { // 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 b *bufio.Writer
status int status int
meta string meta string
@ -139,41 +170,27 @@ type ResponseWriter struct {
} }
// NewResponseWriter returns a ResponseWriter that uses the provided io.Writer. // NewResponseWriter returns a ResponseWriter that uses the provided io.Writer.
func NewResponseWriter(w io.Writer) *ResponseWriter { func NewResponseWriter(w io.Writer) ResponseWriter {
return &ResponseWriter{ return &responseWriter{
b: bufio.NewWriter(w), 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.status = status
w.meta = meta w.meta = meta
} }
// Status sets the response status code. func (w *responseWriter) Status(status int) {
// It also sets the response meta to Meta(status).
func (w *ResponseWriter) Status(status int) {
w.status = status w.status = status
w.meta = Meta(status) w.meta = Meta(status)
} }
// Meta sets the response meta. func (w *responseWriter) Meta(meta string) {
//
// 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) {
w.meta = meta w.meta = meta
} }
// Write writes data to the connection as part of the response body. func (w *responseWriter) Write(b []byte) (int, error) {
// 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) {
if !w.wroteHeader { if !w.wroteHeader {
w.writeHeader(StatusSuccess) w.writeHeader(StatusSuccess)
} }
@ -183,7 +200,7 @@ func (w *ResponseWriter) Write(b []byte) (int, error) {
return w.b.Write(b) return w.b.Write(b)
} }
func (w *ResponseWriter) writeHeader(defaultStatus int) { func (w *responseWriter) writeHeader(defaultStatus int) {
status := w.status status := w.status
if status == 0 { if status == 0 {
status = defaultStatus status = defaultStatus
@ -205,11 +222,7 @@ func (w *ResponseWriter) writeHeader(defaultStatus int) {
w.wroteHeader = true w.wroteHeader = true
} }
// Flush writes any buffered data to the underlying io.Writer. func (w *responseWriter) Flush() error {
//
// 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 {
if !w.wroteHeader { if !w.wroteHeader {
w.writeHeader(StatusTemporaryFailure) w.writeHeader(StatusTemporaryFailure)
} }

View File

@ -83,7 +83,7 @@ func (s *Server) Handle(pattern string, handler Handler) {
} }
// HandleFunc registers the handler function for the given pattern. // 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)) 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 // ServeGemini should write the response header and data to the ResponseWriter
// and then return. // and then return.
type Handler interface { type Handler interface {
ServeGemini(*ResponseWriter, *Request) ServeGemini(ResponseWriter, *Request)
} }
// The HandlerFunc type is an adapter to allow the use of ordinary functions // 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, // as Gemini handlers. If f is a function with the appropriate signature,
// HandlerFunc(f) is a Handler that calls f. // HandlerFunc(f) is a Handler that calls f.
type HandlerFunc func(*ResponseWriter, *Request) type HandlerFunc func(ResponseWriter, *Request)
// ServeGemini calls f(w, r). // ServeGemini calls f(w, r).
func (f HandlerFunc) ServeGemini(w *ResponseWriter, r *Request) { func (f HandlerFunc) ServeGemini(w ResponseWriter, r *Request) {
f(w, r) f(w, r)
} }