chamorrx/servidor.go

141 lines
2.9 KiB
Go

// servidor gemini basado en el ejemplo de go-gemini y algo del código de flounder
// https://git.sr.ht/~adnano/go-gemini
// https://git.alexwennerberg.com/flounder/
package main
import (
"context"
"log"
"os"
"os/signal"
"time"
"io"
"bufio"
"path"
"path/filepath"
"strings"
"git.sr.ht/~adnano/go-gemini"
"git.sr.ht/~adnano/go-gemini/certificate"
)
// de flounder: https://git.alexwennerberg.com/flounder/file/utils.go.html
func cleanPath(thepath string) string {
res := filepath.FromSlash(path.Clean("/" + strings.Trim(thepath, "/")))
if strings.Contains(res, "..") { // sanity check
return ""
}
return res
}
// mapa de hosts : directorios
var mapa map[string]string
// inspirada por gmiPage de flounder: https://git.alexwennerberg.com/flounder/file/gemini.go.html
func gmiHandler(_ context.Context, w gemini.ResponseWriter, r *gemini.Request) {
fileName := cleanPath(r.URL.Path)
// obtiene el hostname sin el puerto
host := strings.SplitN(r.URL.Host, ":", 2)[0]
// utiliza el directorio indicado por el mapa
dir := mapa[host]
fullPath := path.Join(dir, fileName)
if strings.Contains(fileName, "atom.xml") {
_, err := os.Stat(fullPath)
if err == nil {
// inspirado de ServeFile en fs.go:
f, _ := os.Open(fullPath)
defer f.Close()
w.SetMediaType("application/atom+xml")
io.Copy(w, f)
return
}
}
gemini.ServeFile(w, os.DirFS(dir), fileName)
}
func main() {
config, err := os.Open("config.txt")
log.Println("cargando configuración...")
if err != nil {
log.Fatal(err)
}
defer config.Close()
scanner := bufio.NewScanner(config)
mapa = make(map[string]string)
for scanner.Scan(){
campos := strings.SplitN( scanner.Text(), " ", 2 )
mapa[ campos[0] ] = campos[1]
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
log.Println("configuración cargada!")
if err := os.MkdirAll("certs",0700); err != nil {
log.Fatal(err)
}
log.Println("registrando certificados...")
certificates := &certificate.Store{}
for host,_ := range mapa {
certificates.Register(host)
}
if err := certificates.Load("certs"); err != nil {
log.Fatal(err)
}
log.Println("certificados listos!")
log.Println("iniciando servidor...")
mux := &gemini.Mux{}
mux.HandleFunc("/", gmiHandler)
server := &gemini.Server{
Addr: ":1965",
Handler: gemini.LoggingMiddleware(mux),
ReadTimeout: 30 * time.Second,
WriteTimeout: 1 * time.Minute,
GetCertificate: certificates.Get,
}
// Listen for interrupt signal
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
errch := make(chan error)
go func() {
ctx := context.Background()
errch <- server.ListenAndServe(ctx)
}()
select {
case err := <-errch:
log.Fatal(err)
case <-c:
// Shutdown the server
log.Println("Shutting down...")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
err := server.Shutdown(ctx)
if err != nil {
log.Fatal(err)
}
}
}