lots more documentation comments
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
197d8e4cb0
commit
e183f9cd23
|
@ -23,7 +23,7 @@ func main() {
|
||||||
// build the request handler
|
// build the request handler
|
||||||
fileSystem := os.DirFS(".")
|
fileSystem := os.DirFS(".")
|
||||||
// Fallthrough tries each handler in succession until it gets something other than "51 Not Found"
|
// Fallthrough tries each handler in succession until it gets something other than "51 Not Found"
|
||||||
handler := gemini.Fallthrough(
|
handler := gemini.FallthroughHandler(
|
||||||
// first see if they're fetching a directory and we have <dir>/index.gmi
|
// first see if they're fetching a directory and we have <dir>/index.gmi
|
||||||
fs.DirectoryDefault(fileSystem, "index.gmi"),
|
fs.DirectoryDefault(fileSystem, "index.gmi"),
|
||||||
// next (still if they requested a directory) build a directory listing response
|
// next (still if they requested a directory) build a directory listing response
|
||||||
|
|
|
@ -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 also populates the TLSState and RemoteAddr fields on the request - the only field
|
||||||
// it needs populated beforehand is the URL.
|
// 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) {
|
func (client Client) RoundTrip(request *Request) (*Response, error) {
|
||||||
if request.Scheme != "gemini" && request.Scheme != "" {
|
if request.Scheme != "gemini" && request.Scheme != "" {
|
||||||
return nil, errors.New("non-gemini protocols not supported")
|
return nil, errors.New("non-gemini protocols not supported")
|
||||||
|
@ -57,8 +60,8 @@ func (client Client) RoundTrip(request *Request) (*Response, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// read and store the request body in full or we may miss doing so before the
|
// read and store the request body in full or we may miss doing so before
|
||||||
// connection gets closed.
|
// closing the connection
|
||||||
bodybuf, err := io.ReadAll(response.Body)
|
bodybuf, err := io.ReadAll(response.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -6,7 +6,7 @@ import "context"
|
||||||
//
|
//
|
||||||
// A Handler MUST NOT return a nil response. Errors should be returned in the form
|
// 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
|
// 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
|
type Handler func(context.Context, *Request) *Response
|
||||||
|
|
||||||
// Middleware is a handle decorator.
|
// Middleware is a handle decorator.
|
||||||
|
@ -15,7 +15,11 @@ type Handler func(context.Context, *Request) *Response
|
||||||
// transform the request or response in some way.
|
// transform the request or response in some way.
|
||||||
type Middleware func(Handler) Handler
|
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 {
|
return func(ctx context.Context, req *Request) *Response {
|
||||||
for _, handler := range handlers {
|
for _, handler := range handlers {
|
||||||
response := handler(ctx, req)
|
response := handler(ctx, req)
|
||||||
|
@ -27,3 +31,24 @@ func Fallthrough(handlers ...Handler) Handler {
|
||||||
return NotFound("Resource does not exist.")
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,10 +14,26 @@ var InvalidRequestLineEnding = errors.New("invalid request line ending")
|
||||||
|
|
||||||
// Request represents a request over the gemini protocol.
|
// Request represents a request over the gemini protocol.
|
||||||
type Request struct {
|
type Request struct {
|
||||||
|
// URL is the specific URL being fetched by the request.
|
||||||
*url.URL
|
*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
|
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
|
RemoteAddr net.Addr
|
||||||
|
|
||||||
|
// TLSState contains information about the TLS encryption over the connection.
|
||||||
|
//
|
||||||
|
// This includes peer certificates and version information.
|
||||||
TLSState *tls.ConnectionState
|
TLSState *tls.ConnectionState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,6 +114,8 @@ type Response struct {
|
||||||
Meta string
|
Meta string
|
||||||
|
|
||||||
// Body is the response body, if any.
|
// Body is the response body, if any.
|
||||||
|
//
|
||||||
|
// It is not guaranteed to be readable more than once.
|
||||||
Body io.Reader
|
Body io.Reader
|
||||||
|
|
||||||
reader io.Reader
|
reader io.Reader
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Server listens on a network and serves the gemini protocol.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
network string
|
network string
|
||||||
|
@ -18,6 +19,7 @@ type Server struct {
|
||||||
handler Handler
|
handler Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewServer builds a server.
|
||||||
func NewServer(
|
func NewServer(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
tlsConfig *tls.Config,
|
tlsConfig *tls.Config,
|
||||||
|
@ -46,6 +48,10 @@ func NewServer(
|
||||||
//
|
//
|
||||||
// This function will allocate resources which are not cleaned up until
|
// This function will allocate resources which are not cleaned up until
|
||||||
// Close() is called.
|
// 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 {
|
func (s *Server) Serve() error {
|
||||||
s.wg.Add(1)
|
s.wg.Add(1)
|
||||||
defer s.wg.Done()
|
defer s.wg.Done()
|
||||||
|
|
|
@ -2,6 +2,9 @@ package gemini
|
||||||
|
|
||||||
import "crypto/tls"
|
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) {
|
func FileTLS(certfile string, keyfile string) (*tls.Config, error) {
|
||||||
cert, err := tls.LoadX509KeyPair(certfile, keyfile)
|
cert, err := tls.LoadX509KeyPair(certfile, keyfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Reference in New Issue