godlighty

godlighty is highly-customizable HTTP, HTTP/2, HTTPS server written on pure Go.

Why yet another web-server? Because all others suck even for my simple ordinary needs: they use hateful OpenSSL, lacks documentation, has complex configuration format, lack some features, hard to extend with.

Basically all configuration is done directly inside source code. You have to recompile it every time configuration changes. Is it a problem? I doubt, because Go is very fast. But it produces huge statically linked executables, you say! Use bsdiff/bspatch!

Send SIGINFO (SIGUSR1 on Linux) signal to get current daemon’s configuration.

Initially godlighty has basic static files handlers (with compression, HTTP preconditions are enabled of course). In the example below there are nearly all default functions.

Hosts["example.com"] = &godlighty.HostCfg{
    Root: "/www/example.com",
    TLS: &godlighty.TLSCfg{
        Cert: "/path/to/example.com.pem",
        Key: "/path/to/example.com.key.pem",
        CACert: "/path/to/ca.pem",
    },
    DirList: true,
    WebDAV: true,
    MIMEs: map[string]string{
        ".special": "text/x-special-type",
    },
}

If your keys and certificates are in single file and you use common CA certificate, then it is trivial to DRY:

var (
    Etc = "/whatever"
    CACert = path.Join(Etc, "ca.pem")
)

func newTLSCfg(host string) *godlighty.TLSCfg {
    return &godlighty.TLSCfg{
        Cert:   path.Join(Etc, host+".pem"),
        Key:    path.Join(Etc, host+".pem"),
        CACert: CACert,
    }
}

But there are hooks that can do anything more. For example if you want to run CGI script or make a redirection (FastCGI handler example is in rc/fcgi):

HostCfg{
    ...,
    Hooks: []godlighty.Hook{
        func(w http.ResponseWriter, r *http.Request) bool {
            if r.URL.Path == "/" {
                http.Redirect(w, r, "//here.we.go/", http.StatusMovedPermanently)
                return true
            }
            return false
        },
        func(w http.ResponseWriter, r *http.Request) bool {
            cgi.Handler{
                Path: "/usr/local/libexec/git-core/git-http-backend",
                Dir:  "/var/empty",
                Env: []string{
                    "GIT_PROJECT_ROOT=" + root,
                    "GIT_HTTP_EXPORT_ALL=",
                },
            }.ServeHTTP(w, r)
            return true
        },
    },

You can separate your configuration files and add them through init() call:

$ ls rc
blog.stargrave.org.go
ca.cypherpunks.ru.go
cgi.go
cryptoanarchy.ru.go
git.go
go.go
if.mirror.cypherpunks.ru.go
lists.cypherpunks.ru.go
mime.go
static.go
tls.go
[...]

$ cat rc/static
package rc

import "path"

func addStaticCfg(host, root string) {
    if !path.IsAbs(root) {
        root = path.Join(WWW, root)
    }
    godlighty.Hosts[host] = &godlighty.HostCfg{
        Root: root,
        TLS:  newTLSCfg(host),
    }
}

func addStaticListedDir(host, root string) {
    addStaticCfg(host, root)
    godlighty.Hosts[host].DirList = true
    godlighty.Hosts[host].WebDAV = true
}

func init() {
    [...]
    addStaticCfg("paster.stargrave.org", "/storage/paster/pastes")
    addStaticCfg("www.godlighty.stargrave.org", "godlighty.stargrave.org")
    addStaticCfg("www.nncpgo.org", "nncpgo.org")
    addStaticListedDir("www.mds.cypherpunks.ru", "/storage/audiobook/mds")
    [...]
}

It is created exclusively to drive my own websites, but if you are interested, then git clone git://git.stargrave.org/godlighty.git it.