141 lines
2.9 KiB
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)
|
|
}
|
|
}
|
|
}
|