Simplify ResponseWriter implementation
This commit is contained in:
parent
3ed39e62d8
commit
d01d50ff1a
4
fs.go
4
fs.go
@ -39,7 +39,7 @@ func (fsh fsHandler) Respond(w *ResponseWriter, r *Request) {
|
|||||||
// Detect mimetype
|
// Detect mimetype
|
||||||
ext := path.Ext(p)
|
ext := path.Ext(p)
|
||||||
mimetype := mime.TypeByExtension(ext)
|
mimetype := mime.TypeByExtension(ext)
|
||||||
w.SetMediaType(mimetype)
|
w.Meta(mimetype)
|
||||||
// Copy file to response writer
|
// Copy file to response writer
|
||||||
_, _ = io.Copy(w, f)
|
_, _ = io.Copy(w, f)
|
||||||
}
|
}
|
||||||
@ -78,7 +78,7 @@ func ServeFile(w *ResponseWriter, fs FS, name string) {
|
|||||||
// Detect mimetype
|
// Detect mimetype
|
||||||
ext := path.Ext(name)
|
ext := path.Ext(name)
|
||||||
mimetype := mime.TypeByExtension(ext)
|
mimetype := mime.TypeByExtension(ext)
|
||||||
w.SetMediaType(mimetype)
|
w.Meta(mimetype)
|
||||||
// Copy file to response writer
|
// Copy file to response writer
|
||||||
_, _ = io.Copy(w, f)
|
_, _ = io.Copy(w, f)
|
||||||
}
|
}
|
||||||
|
89
response.go
89
response.go
@ -114,3 +114,92 @@ func (b *readCloserBody) Read(p []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
return b.ReadCloser.Read(p)
|
return b.ReadCloser.Read(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResponseWriter is used by a Gemini handler to construct a Gemini response.
|
||||||
|
type ResponseWriter struct {
|
||||||
|
b *bufio.Writer
|
||||||
|
status Status
|
||||||
|
meta string
|
||||||
|
setHeader bool
|
||||||
|
wroteHeader bool
|
||||||
|
bodyAllowed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewResponseWriter returns a ResponseWriter that uses the provided io.Writer.
|
||||||
|
func NewResponseWriter(w io.Writer) *ResponseWriter {
|
||||||
|
return &ResponseWriter{
|
||||||
|
b: bufio.NewWriter(w),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header sets the response header.
|
||||||
|
func (w *ResponseWriter) Header(status Status, meta string) {
|
||||||
|
w.status = status
|
||||||
|
w.meta = meta
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status sets the response status code.
|
||||||
|
// It also sets the response meta to status.Meta().
|
||||||
|
func (w *ResponseWriter) Status(status Status) {
|
||||||
|
w.status = status
|
||||||
|
w.meta = status.Meta()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
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) {
|
||||||
|
if !w.wroteHeader {
|
||||||
|
w.writeHeader(StatusSuccess)
|
||||||
|
}
|
||||||
|
if !w.bodyAllowed {
|
||||||
|
return 0, ErrBodyNotAllowed
|
||||||
|
}
|
||||||
|
return w.b.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *ResponseWriter) writeHeader(defaultStatus Status) {
|
||||||
|
status := w.status
|
||||||
|
if status == 0 {
|
||||||
|
status = defaultStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
meta := w.meta
|
||||||
|
if status.Class() == StatusClassSuccess {
|
||||||
|
w.bodyAllowed = true
|
||||||
|
|
||||||
|
if meta == "" {
|
||||||
|
meta = "text/gemini"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.b.WriteString(strconv.Itoa(int(status)))
|
||||||
|
w.b.WriteByte(' ')
|
||||||
|
w.b.WriteString(meta)
|
||||||
|
w.b.Write(crlf)
|
||||||
|
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 {
|
||||||
|
if !w.wroteHeader {
|
||||||
|
w.writeHeader(StatusTemporaryFailure)
|
||||||
|
}
|
||||||
|
// Write errors from writeHeader will be returned here.
|
||||||
|
return w.b.Flush()
|
||||||
|
}
|
||||||
|
108
server.go
108
server.go
@ -1,11 +1,8 @@
|
|||||||
package gemini
|
package gemini
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
@ -236,111 +233,6 @@ func (s *Server) logf(format string, args ...interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResponseWriter is used by a Gemini handler to construct a Gemini response.
|
|
||||||
type ResponseWriter struct {
|
|
||||||
status Status
|
|
||||||
meta string
|
|
||||||
b *bufio.Writer
|
|
||||||
bodyAllowed bool
|
|
||||||
wroteHeader bool
|
|
||||||
mediatype string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewResponseWriter returns a ResponseWriter that uses the provided io.Writer.
|
|
||||||
func NewResponseWriter(w io.Writer) *ResponseWriter {
|
|
||||||
return &ResponseWriter{
|
|
||||||
b: bufio.NewWriter(w),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Header sets the response header.
|
|
||||||
//
|
|
||||||
// Meta contains more information related to the response status.
|
|
||||||
// For successful responses, Meta should contain the mimetype of the response.
|
|
||||||
// For failure responses, Meta should contain a short description of the failure.
|
|
||||||
// Meta should not be longer than 1024 bytes.
|
|
||||||
func (w *ResponseWriter) Header(status Status, meta string) {
|
|
||||||
w.status = status
|
|
||||||
w.meta = meta
|
|
||||||
}
|
|
||||||
|
|
||||||
// Status sets the response header to the given status code.
|
|
||||||
//
|
|
||||||
// Status is equivalent to Header(status, status.Meta())
|
|
||||||
func (w *ResponseWriter) Status(status Status) {
|
|
||||||
w.status = status
|
|
||||||
w.meta = status.Meta()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetMediaType sets the media type that will be written for a successful response.
|
|
||||||
// If the mimetype is not set, it will default to "text/gemini".
|
|
||||||
func (w *ResponseWriter) SetMediaType(mediatype string) {
|
|
||||||
w.mediatype = mediatype
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
// If the response header has not yet been written, Write calls WriteHeader
|
|
||||||
// with StatusSuccess and the mimetype set in SetMimetype.
|
|
||||||
func (w *ResponseWriter) Write(b []byte) (int, error) {
|
|
||||||
if !w.wroteHeader {
|
|
||||||
err := w.writeHeader()
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !w.bodyAllowed {
|
|
||||||
return 0, ErrBodyNotAllowed
|
|
||||||
}
|
|
||||||
|
|
||||||
return w.b.Write(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *ResponseWriter) writeHeader() error {
|
|
||||||
status := w.status
|
|
||||||
if status == 0 {
|
|
||||||
status = StatusSuccess
|
|
||||||
}
|
|
||||||
|
|
||||||
meta := w.meta
|
|
||||||
|
|
||||||
if status.Class() == StatusClassSuccess {
|
|
||||||
w.bodyAllowed = true
|
|
||||||
|
|
||||||
if meta == "" {
|
|
||||||
meta = w.mediatype
|
|
||||||
}
|
|
||||||
|
|
||||||
if meta == "" {
|
|
||||||
meta = "text/gemini"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := fmt.Fprintf(w.b, "%d %s\r\n", int(status), meta)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to write response header: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
w.wroteHeader = true
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush writes any buffered data to the underlying io.Writer.
|
|
||||||
func (w *ResponseWriter) Flush() error {
|
|
||||||
if !w.wroteHeader {
|
|
||||||
err := w.writeHeader()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return w.b.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Responder responds to a Gemini request.
|
// A Responder responds to a Gemini request.
|
||||||
type Responder interface {
|
type Responder interface {
|
||||||
// Respond accepts a Request and constructs a Response.
|
// Respond accepts a Request and constructs a Response.
|
||||||
|
Loading…
Reference in New Issue
Block a user