Documentation ¶
Overview ¶
Package gemini provides a server implementation for the Gemini protocol.
This package should feel familiar to people comfortable with Go's net/http however it differs where it makes sense.
Handlers receive a context directly rather than attaching it to a request.
There is no support for starting a server without TLS.
The ResponseWriter provides methods for handing different response types rather than a single abstract response.
An example of a server using this package. The server greets a user, accepting input from either a prompt or a client cert. The example includes a graceful shutdown, log handler, and a shutdown func:
package main import ( "context" "fmt" "log" "os" "os/signal" "source.community/ckaznocha/gemini" ) func greetHandler(ctx context.Context, w gemini.ResponseWriter, r *gemini.Request) { var name string switch { case r.Subject != nil: name = r.Subject.CommonName case r.URI.Query != "": name = r.URI.RawQuery default: w.Input(ctx, "What is your name?", false) return } fmt.Fprintf(w.Success(ctx, ""), "Hello, %s!", name) } func main() { logger := log.New( os.Stdout, "[Gemini Example] ", log.LstdFlags|log.LUTC|log.Lmicroseconds|log.Lmsgprefix|log.Lshortfile, ) logger.Println("Server starting") mux := gemini.NewServeMux() mux.HandleFunc("/greet", greetHandler) s := &gemini.Server{ Handler: mux, LogHandler: func(message string, isError bool) { logger.Printf("gemini server: %s", message) }, } s.RegisterOnShutdown(func() { s.LogHandler("shutting down", false) }) ctx, cancel := context.WithCancel(context.Background()) defer cancel() logger.Println("Server started") go func() { defer cancel() s.LogHandler("starting", false) err := s.ListenAndServeTLS("", "testdata/cert.pem", "testdata/key.pem") s.LogHandler(fmt.Sprintf("exited: %s\n", err), true) }() ctx, stop := signal.NotifyContext(ctx, os.Interrupt) defer stop() <-ctx.Done() logger.Println("Shutdown starting") defer logger.Println("Shutdown complete") if err := s.Shutdown(context.Background()); err != nil { logger.Printf("Error during shutdown: %s\n", err) } }
Index ¶
- Variables
- type Handler
- type HandlerFunc
- type Request
- type ResponseWriter
- type ServeMux
- func (sm *ServeMux) Handle(pattern string, handler Handler)
- func (sm *ServeMux) HandleFunc(pattern string, handler func(context.Context, ResponseWriter, *Request))
- func (sm *ServeMux) Handler(r *Request) (h Handler, pattern string)
- func (sm *ServeMux) ServeGemini(ctx context.Context, w ResponseWriter, r *Request)
- type Server
- type StatusCode
- type StatusCodeCategory
- type URI
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // Listener sentinels. ErrClosingListeners = errors.New("error closing listeners") ErrClosingListener = errors.New("error closing listener") ErrOpeningConnection = errors.New("error opening connection") // Request sentinels. ErrMaxRequestLengthExceeded = fmt.Errorf( "the request length exceeded the max size of %d bytes", maxRequestLength, ) ErrRequestRead = errors.New("unable to read request") // Server sentinels. ErrServerShutdown = errors.New("server shutdown") ErrStartingServer = errors.New("unable to start server") ErrServing = errors.New("unable to serve requests") ErrIO = errors.New("IO error") // URL sentinels. ErrMalformedURI = errors.New("malformed URI") )
Sentinel errors.
Functions ¶
This section is empty.
Types ¶
type Handler ¶
type Handler interface {
ServeGemini(context.Context, ResponseWriter, *Request)
}
Handler is an interface that handles Gemini incoming requests.
type HandlerFunc ¶
type HandlerFunc func(context.Context, ResponseWriter, *Request)
HandlerFunc is an adapter which allows a function to be used as a Handler.
func (HandlerFunc) ServeGemini ¶
func (f HandlerFunc) ServeGemini(ctx context.Context, w ResponseWriter, r *Request)
ServeGemini implements the Hander interface for a HandlerFunc.
type Request ¶
Request is a Gemini request.
type ResponseWriter ¶
type ResponseWriter interface { Failure(ctx context.Context, code StatusCode, msg string) Input(ctx context.Context, prompt string, isSensitive bool) Redirect(ctx context.Context, redirectURL string, isPermanant bool) Success(ctx context.Context, mimeType string) io.Writer }
ResponseWriter is interface to interact with a Gemini response. Calling any of its meethods after one has already been called or after Handler.ServeGemini has returned is a no-op.
type ServeMux ¶
type ServeMux struct {
// contains filtered or unexported fields
}
ServeMux is a Gemini request multiplexer. It will match requests to handlers based on the URI path. The longest match will be the one returned. Patterns ending with `/` will be matched exactly. Patterns without a trailing `/` will be treated as a prefix match.
func NewServeMux ¶
func NewServeMux() *ServeMux
NewServeMux returns a new ServeMux ready to be used.
func (*ServeMux) Handle ¶
Handle adds a new pattern/Handler pair to the ServeMux.
func (*ServeMux) HandleFunc ¶
func (sm *ServeMux) HandleFunc(pattern string, handler func(context.Context, ResponseWriter, *Request))
HandleFunc adds a new pattern/HandleFunc pair to the ServeMux.
func (*ServeMux) Handler ¶
Handler looks up a matching Handler based on a Request. It returns the patter that matched in addition to the Hander.
type Server ¶
type Server struct { // Handler is a handler that is called each time a new network requests is // received. Unlike Go's net/http panics in handlers will not be recovered // automatically. Handler Handler // LogHandler is an optional function that allows a custom logger to be // hooked into the server. Erroneous logs will be passed in with `isError` // set to true. LogHandler func(message string, isError bool) // BaseContext is a optional function that takes a listener and returns a // context. The context returned by BaseContext will be used to create all // other contexts in the request lifecycle. BaseContext func(net.Listener) context.Context // ConnContext is an optional function that takes a context and a net.Conn // and returns a context. Like BaseContext, the context returned by // ConnContext will be used to create all contexts in the request lifecycle // after the connection has been created. ConnContext func(ctx context.Context, c net.Conn) context.Context // TLSConfig is the TLS config to use for the server. TLSConfig *tls.Config // contains filtered or unexported fields }
Server serves network requests using the Gemini protocol.
func ServerFromCtx ¶
ServerFromCtx extracts a server from a context if present. If a server is not present on the context the returned bool will be false.
func (*Server) ListenAndServeTLS ¶
ListenAndServeTLS creates a listener and starts the server. If certFile and keyFile are non-empty strings the key pair will be loaded and used.
Example ¶
package main import ( "context" "fmt" "log" "source.community/ckaznocha/gemini" ) func main() { s := &gemini.Server{ Handler: gemini.HandlerFunc(func(ctx context.Context, w gemini.ResponseWriter, r *gemini.Request) { fmt.Fprintln(w.Success(ctx, ""), "Hello, TLS!") }), } // One can use generate_cert.go in crypto/tls to generate cert.pem and key.pem. log.Printf("About to listen the default port. Go to gemini://127.0.0.1/") err := s.ListenAndServeTLS("", "cert.pem", "key.pem") log.Fatal(err) }
Output:
func (*Server) RegisterOnShutdown ¶
func (s *Server) RegisterOnShutdown(f func())
RegisterOnShutdown adds a function which will be called when the server shuts down. RegisterOnShutdown can be called more than once to stack functions.
func (*Server) Serve ¶
Serve start a server using the provided listener. The listener should support TLS.
func (*Server) ServeTLS ¶
ServeTLS starts a server with the provided listener, wrapping it in a TLS listener. If certFile and keyFile are non-empty strings the key pair will be loaded and used.
type StatusCode ¶
type StatusCode uint8
StatusCode represents a Gemini status code. Gemini status codes are two digit values. See `Project Gemini - Speculative specification - Section 3.2`.
const ( // 1x - INPUT StatusInput StatusCode = 10 // INPUT StatusSensitiveInput StatusCode = 11 // SENSITIVE INPUT // 2x - SUCCESS StatusSuccess StatusCode = 20 // SUCCESS // 3x - REDIRECT StatusTemporaryRedirect StatusCode = 30 // REDIRECT - TEMPORARY StatusPermanentRedirect StatusCode = 31 // REDIRECT - PERMANENT // 4x - TEMPORARY FAILURE StatusTemporaryFailure StatusCode = 40 // TEMPORARY FAILURE StatusServerFailure StatusCode = 41 // SERVER UNAVAILABLE StatusCGIError StatusCode = 42 // CGI ERROR StatusProxyError StatusCode = 43 // PROXY ERROR StatusSlowDown StatusCode = 44 // SLOW DOWN // 5x - PERMANENT FAILURE StatusPermanentFailure StatusCode = 50 // PERMANENT FAILURE StatusNotFound StatusCode = 51 // NOT FOUND StatusGone StatusCode = 52 // GONE StatusProxyRequestRefused StatusCode = 53 // PROXY REQUEST REFUSED StatusBadRequest StatusCode = 59 // BAD REQUEST // 6x - CLIENT CERTIFICATE REQUIRED StatusClientCertificateRequired StatusCode = 60 // CLIENT CERTIFICATE REQUIRED StatusCertificateNotAuthorised StatusCode = 61 // CERTIFICATE NOT AUTHORIZED StatusCertificateNotValid StatusCode = 62 // CERTIFICATE NOT VALID )
The status codes as defined in `Project Gemini - Speculative specification - Appendix 1. Full two digit status codes`.
func (StatusCode) Description ¶
func (c StatusCode) Description() string
Description returns the description of the code as described in `Project Gemini - Speculative specification`. Some but not all of the descriptions may be appropriate to return to a client as a failure description.
func (StatusCode) MarshalText ¶
func (c StatusCode) MarshalText() ([]byte, error)
MarshalText implements the encoding.TextMarshaler interface. It returns the the code as an ASCII byte slice. It returns "00" if the code is invalid.
func (StatusCode) String ¶
func (i StatusCode) String() string
func (StatusCode) ToCategory ¶
func (c StatusCode) ToCategory() StatusCodeCategory
ToCategory returns which category the status code belongs to.
type StatusCodeCategory ¶
type StatusCodeCategory uint8
StatusCodeCategory identifies a class of Gemini status codes.
const ( StatusCategoryInput StatusCodeCategory = 1 // INPUT StatusCategorySuccess StatusCodeCategory = 2 // SUCCESS StatusCategoryRedirect StatusCodeCategory = 3 // REDIRECT StatusCategoryTemporaryFailure StatusCodeCategory = 4 // TEMPORARY FAILURE StatusCategoryPermanentFailure StatusCodeCategory = 5 // PERMANENT FAILURE StatusCategoryClientCertificateRequired StatusCodeCategory = 6 // CLIENT CERTIFICATE REQUIRED StatusCategoryUndefined StatusCodeCategory = 0 // UNDEFINED StatusCategoryUndefinedX StatusCodeCategory = 7 // UNDEFINED StatusCategoryUndefinedXX StatusCodeCategory = 8 // UNDEFINED StatusCategoryUndefinedXXX StatusCodeCategory = 9 // UNDEFINED )
The status code categories as defined in `Project Gemini - Speculative specification - Section 3.2`.
func (StatusCodeCategory) String ¶
func (i StatusCodeCategory) String() string
func (StatusCodeCategory) ToCode ¶
func (c StatusCodeCategory) ToCode() StatusCode
ToCode converts a Categoy to the base code for that category.
type URI ¶
type URI struct { Host string Port string Path string Fragment string // Query is the decoded query section. The original query is stored in // RawQuery. Query string // RawQuery is the original query section as received by the server. Use // this in the event you need to parse a query section into a url.Values. RawQuery string // contains filtered or unexported fields }
URI represents a Gemini URI.
Resources hosted via Gemini are identified using URIs with the scheme "gemini". This scheme is syntactically compatible with the generic URI syntax defined in RFC 3986, but does not support all components of the generic syntax. In particular, the authority component is allowed and required, but its userinfo subcomponent is NOT allowed. The host subcomponent is required. The port subcomponent is optional, with a default value of 1965. The path, query and fragment components are allowed and have no special meanings beyond those defined by the generic syntax. Spaces in gemini URIs should be encoded as %20, not +.
func ParseRequestURI ¶
ParseRequestURI parses a raw URI string into a Gemini URI. It returns an error if the URI is invalid.
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
Package geminitest provides a test server and functions to help write tests for gemini servers.
|
Package geminitest provides a test server and functions to help write tests for gemini servers. |