parent
0bd8bd6f23
commit
84c1322c44
@ -1,2 +1,122 @@ |
|||||||
// Package gmi contains utilities for working with gemini and gemtext
|
// Package gmi implements the gemini-based api for the mediocre-blog.
|
||||||
package gmi |
package gmi |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
"net/url" |
||||||
|
"os" |
||||||
|
"strings" |
||||||
|
|
||||||
|
"git.sr.ht/~adnano/go-gemini" |
||||||
|
"git.sr.ht/~adnano/go-gemini/certificate" |
||||||
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/cfg" |
||||||
|
"github.com/mediocregopher/mediocre-go-lib/v2/mctx" |
||||||
|
"github.com/mediocregopher/mediocre-go-lib/v2/mlog" |
||||||
|
) |
||||||
|
|
||||||
|
// Params are used to instantiate a new API instance. All fields are required
|
||||||
|
// unless otherwise noted.
|
||||||
|
type Params struct { |
||||||
|
Logger *mlog.Logger |
||||||
|
PublicURL *url.URL |
||||||
|
ListenAddr string |
||||||
|
CertificatesPath string |
||||||
|
} |
||||||
|
|
||||||
|
// SetupCfg implement the cfg.Cfger interface.
|
||||||
|
func (p *Params) SetupCfg(cfg *cfg.Cfg) { |
||||||
|
|
||||||
|
publicURLStr := cfg.String("gemini-public-url", "gemini://localhost:2065", "URL this service is accessible at") |
||||||
|
|
||||||
|
cfg.StringVar(&p.ListenAddr, "gemini-listen-addr", ":2065", "Address to listen for HTTP requests on") |
||||||
|
|
||||||
|
cfg.StringVar(&p.CertificatesPath, "gemini-certificates-path", "", "Path to directory where gemini certs should be created/stored") |
||||||
|
|
||||||
|
cfg.OnInit(func(context.Context) error { |
||||||
|
|
||||||
|
if p.CertificatesPath == "" { |
||||||
|
return errors.New("-gemini-certificates-path is required") |
||||||
|
} |
||||||
|
|
||||||
|
var err error |
||||||
|
|
||||||
|
*publicURLStr = strings.TrimSuffix(*publicURLStr, "/") |
||||||
|
|
||||||
|
if p.PublicURL, err = url.Parse(*publicURLStr); err != nil { |
||||||
|
return fmt.Errorf("parsing -gemini-public-url: %w", err) |
||||||
|
} |
||||||
|
|
||||||
|
return nil |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
// Annotate implements mctx.Annotator interface.
|
||||||
|
func (p *Params) Annotate(a mctx.Annotations) { |
||||||
|
a["geminiPublicURL"] = p.PublicURL |
||||||
|
a["geminiListenAddr"] = p.ListenAddr |
||||||
|
a["geminiCertificatesPath"] = p.CertificatesPath |
||||||
|
} |
||||||
|
|
||||||
|
// API will listen on the port configured for it, and serve gemini requests for
|
||||||
|
// the mediocre-blog.
|
||||||
|
type API interface { |
||||||
|
Shutdown(ctx context.Context) error |
||||||
|
} |
||||||
|
|
||||||
|
type api struct { |
||||||
|
params Params |
||||||
|
srv *gemini.Server |
||||||
|
} |
||||||
|
|
||||||
|
// New initializes and returns a new API instance, including setting up all
|
||||||
|
// listening ports.
|
||||||
|
func New(params Params) (API, error) { |
||||||
|
|
||||||
|
if err := os.MkdirAll(params.CertificatesPath, 0700); err != nil { |
||||||
|
return nil, fmt.Errorf("creating certificate directory %q: %w", params.CertificatesPath, err) |
||||||
|
} |
||||||
|
|
||||||
|
certStore := new(certificate.Store) |
||||||
|
certStore.Load(params.CertificatesPath) |
||||||
|
certStore.Register(params.PublicURL.Hostname()) |
||||||
|
|
||||||
|
a := &api{ |
||||||
|
params: params, |
||||||
|
} |
||||||
|
|
||||||
|
a.srv = &gemini.Server{ |
||||||
|
Addr: params.ListenAddr, |
||||||
|
Handler: a.handler(), |
||||||
|
GetCertificate: certStore.Get, |
||||||
|
} |
||||||
|
|
||||||
|
go func() { |
||||||
|
|
||||||
|
ctx := mctx.WithAnnotator(context.Background(), &a.params) |
||||||
|
|
||||||
|
err := a.srv.ListenAndServe(ctx) |
||||||
|
if err != nil && !errors.Is(err, context.Canceled) { |
||||||
|
a.params.Logger.Fatal(ctx, "serving gemini server", err) |
||||||
|
} |
||||||
|
}() |
||||||
|
|
||||||
|
return a, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (a *api) Shutdown(ctx context.Context) error { |
||||||
|
return a.srv.Shutdown(ctx) |
||||||
|
} |
||||||
|
|
||||||
|
func (a *api) handler() gemini.Handler { |
||||||
|
return gemini.HandlerFunc(func( |
||||||
|
ctx context.Context, |
||||||
|
rw gemini.ResponseWriter, |
||||||
|
r *gemini.Request, |
||||||
|
) { |
||||||
|
fmt.Fprintf(rw, "# Test\n\n") |
||||||
|
fmt.Fprintf(rw, "HELLO WORLD\n\n") |
||||||
|
fmt.Fprintf(rw, "=> gemini://midnight.pub Hit the pub\n\n") |
||||||
|
}) |
||||||
|
} |
||||||
|
Loading…
Reference in new issue