From e183f9cd23380a81071c32f64c91e60f46a7d8cb Mon Sep 17 00:00:00 2001 From: tjpcc Date: Wed, 11 Jan 2023 10:36:56 -0700 Subject: lots more documentation comments --- gemini/client.go | 7 +++++-- gemini/handler.go | 29 +++++++++++++++++++++++++++-- gemini/request.go | 16 ++++++++++++++++ gemini/response.go | 2 ++ gemini/serve.go | 6 ++++++ gemini/tls.go | 3 +++ 6 files changed, 59 insertions(+), 4 deletions(-) (limited to 'gemini') diff --git a/gemini/client.go b/gemini/client.go index 53c8b71..aca4576 100644 --- a/gemini/client.go +++ b/gemini/client.go @@ -28,6 +28,9 @@ func NewClient(tlsConf *tls.Config) Client { // // It also populates the TLSState and RemoteAddr fields on the request - the only field // it needs populated beforehand is the URL. +// +// This method will not automatically follow redirects or cache permanent failures or +// redirects. func (client Client) RoundTrip(request *Request) (*Response, error) { if request.Scheme != "gemini" && request.Scheme != "" { return nil, errors.New("non-gemini protocols not supported") @@ -57,8 +60,8 @@ func (client Client) RoundTrip(request *Request) (*Response, error) { return nil, err } - // read and store the request body in full or we may miss doing so before the - // connection gets closed. + // read and store the request body in full or we may miss doing so before + // closing the connection bodybuf, err := io.ReadAll(response.Body) if err != nil { return nil, err diff --git a/gemini/handler.go b/gemini/handler.go index ded77a5..0f48e62 100644 --- a/gemini/handler.go +++ b/gemini/handler.go @@ -6,7 +6,7 @@ import "context" // // A Handler MUST NOT return a nil response. Errors should be returned in the form // of error responses (4x, 5x, 6x response status). If the Handler should not be -// responsible for the requested resource it can return a "51 Not Found" response. +// responsible for the requested resource it can return a 51 response. type Handler func(context.Context, *Request) *Response // Middleware is a handle decorator. @@ -15,7 +15,11 @@ type Handler func(context.Context, *Request) *Response // transform the request or response in some way. type Middleware func(Handler) Handler -func Fallthrough(handlers ...Handler) Handler { +// FallthroughHandler builds a handler which tries multiple child handlers. +// +// The returned handler will invoke each of the passed child handlers in order, +// stopping when it receives a response with status other than 51. +func FallthroughHandler(handlers ...Handler) Handler { return func(ctx context.Context, req *Request) *Response { for _, handler := range handlers { response := handler(ctx, req) @@ -27,3 +31,24 @@ func Fallthrough(handlers ...Handler) Handler { return NotFound("Resource does not exist.") } } + +// Filter wraps a handler with a predicate which determines whether to run the handler. +// +// When the predicate function returns false, the Filter returns the provided failure +// response. The failure argument may be nil, in which case a "51 Resource does not exist." +// response will be used. +func Filter( + predicate func(context.Context, *Request) bool, + handler Handler, + failure *Response, +) Handler { + if failure == nil { + failure = NotFound("Resource does not exist.") + } + return func(ctx context.Context, req *Request) *Response { + if !predicate(ctx, req) { + return failure + } + return handler(ctx, req) + } +} diff --git a/gemini/request.go b/gemini/request.go index 43ee69b..933281b 100644 --- a/gemini/request.go +++ b/gemini/request.go @@ -14,10 +14,26 @@ var InvalidRequestLineEnding = errors.New("invalid request line ending") // Request represents a request over the gemini protocol. type Request struct { + // URL is the specific URL being fetched by the request. *url.URL + // Server is the server which received the request. + // + // This is only populated in gemini servers. + // It is unused on the client end. Server *Server + + // RemoteAddr is the address of the other side of the connection. + // + // This will be the server address for clients, or the connecting + // client's address in servers. + // + // Be aware though that proxies (and reverse proxies) can confuse this. RemoteAddr net.Addr + + // TLSState contains information about the TLS encryption over the connection. + // + // This includes peer certificates and version information. TLSState *tls.ConnectionState } diff --git a/gemini/response.go b/gemini/response.go index 1fa64cf..5b5ced4 100644 --- a/gemini/response.go +++ b/gemini/response.go @@ -114,6 +114,8 @@ type Response struct { Meta string // Body is the response body, if any. + // + // It is not guaranteed to be readable more than once. Body io.Reader reader io.Reader diff --git a/gemini/serve.go b/gemini/serve.go index abf127e..f9a8a1c 100644 --- a/gemini/serve.go +++ b/gemini/serve.go @@ -8,6 +8,7 @@ import ( "sync" ) +// Server listens on a network and serves the gemini protocol. type Server struct { ctx context.Context network string @@ -18,6 +19,7 @@ type Server struct { handler Handler } +// NewServer builds a server. func NewServer( ctx context.Context, tlsConfig *tls.Config, @@ -46,6 +48,10 @@ func NewServer( // // This function will allocate resources which are not cleaned up until // Close() is called. +// +// It will respect cancellation of the context the server was created with, +// but be aware that Close() must still be called in that case to avoid +// dangling goroutines. func (s *Server) Serve() error { s.wg.Add(1) defer s.wg.Done() diff --git a/gemini/tls.go b/gemini/tls.go index 3cdf93b..5b35fb6 100644 --- a/gemini/tls.go +++ b/gemini/tls.go @@ -2,6 +2,9 @@ package gemini import "crypto/tls" +// FileTLS builds a TLS configuration from paths to a certificate and key file. +// +// It sets parameters on the configuration to make it suitable for use with gemini. func FileTLS(certfile string, keyfile string) (*tls.Config, error) { cert, err := tls.LoadX509KeyPair(certfile, keyfile) if err != nil { -- cgit v1.2.3