~alazarte/gemserve

14a02b65ffdc2fe3f3a7ca342ead4ebc72316f91 — Alejandro Lazarte 3 months ago master
Initial commit
2 files changed, 121 insertions(+), 0 deletions(-)

A config
A main.go
A  => config +4 -0
@@ 1,4 @@
Pem=cert.pem
Key=key.pem
Gmipath=/path/to/gmi


A  => main.go +117 -0
@@ 1,117 @@
package main

import (
	"crypto/tls"
	"fmt"
	"io"
	"log"
	"os"
	"regexp"

	"git.sr.ht/~alazarte/cfgreader"
)

var (
	readBufLen = 100

	StatusSuccess = "20 text/gemini\r\n"
	StatusGone    = "51 GONE\r\n"
)

type config struct {
	Pem     string
	Key     string
	Gmipath string
}

func filename(r string) string {
	matchFilename := regexp.MustCompile(`gemini://alazarte.com/([a-z_]+.gmi)`)
	matches := matchFilename.FindStringSubmatch(r)
	if len(matches) != 2 {
		log.Println("I don't know how to regex, so assume index.gmi")
		return "index.gmi"
	}
	return matches[1]
}

type handler struct {
	root   string
	config *tls.Config
	addr   string
}

func New(addr string, root string, pem string, key string) (handler, error) {
	certPem, err := os.ReadFile(pem)
	if err != nil {
		return handler{}, err
	}
	keyPem, err := os.ReadFile(key)
	if err != nil {
		return handler{}, err
	}
	cert, err := tls.X509KeyPair(certPem, keyPem)
	if err != nil {
		return handler{}, err
	}
	return handler{
		config: &tls.Config{Certificates: []tls.Certificate{cert}},
		addr:   addr,
		root:   root,
	}, nil
}

func (h *handler) Serve() error {
	l, err := tls.Listen("tcp", h.addr, h.config)
	if err != nil {
		return err
	}
	for {
		buf := make([]byte, readBufLen)
		conn, err := l.Accept()
		if err != nil {
			log.Printf("failed to accept connection: [err=%s]", err)
			continue
		}
		conn.Read(buf)
		filename := fmt.Sprintf("%s/%s", h.root, filename(string(buf)))
		f, err := os.Open(filename)
		if err == nil {
			if _, err := conn.Write([]byte(StatusSuccess)); err == nil {
				if _, err := io.Copy(conn, f); err != nil {
					log.Printf("failed to write body [err=%s]", err)
				}
			} else {
				log.Printf("failed to write header [header=%s err=%s]", StatusSuccess, err)
			}
		} else {
			if _, err := conn.Write([]byte(StatusGone)); err != nil {
				log.Printf("failed to write header [header=%s err=%s]", StatusGone, err)
			}
			log.Printf("failed to open file: [err=%s]", err)
		}
		conn.Close()
		continue
	}
}

func readConfig(filepath string) (config, error) {
	c := config{}
	f, err := os.Open(filepath)
	if err != nil {
		return c, err
	}
	err = cfgreader.ReadFrom(f, &c)
	return c, err
}

func main() {
	c, err := readConfig("config")
	if err != nil {
		panic(err)
	}
	h, err := New(":1965", c.Gmipath, c.Pem, c.Key)
	if err != nil {
		panic(err)
	}
	log.Panic(h.Serve())
}