Shortlinks and go-get redirects
git clone git://git.danielmoch.com/go-shrt.git
Log | Files | Refs | README | LICENSE

commit abd45c8bb0d1a36197f7782f8afd05c58aa9ceb6
parent 415f91390f5fba22aa9b2d9d43bf35bfc50ae81c
Author: Daniel Moch <daniel@danielmoch.com>
Date:   Sun,  6 Dec 2020 19:37:30 -0500

Add manpages; improve readme

M.gitignore | 4+++-
MMakefile | 33+++++++++++++++++++++++++++------
MREADME | 36+++++++++++++++++++++++++++++++++++-
Mcmd/shrt/main.go | 38++++++++++++++++++++++++--------------
Aconfig.mk | 9+++++++++
Ashrt.1 | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ashrtfile.5 | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 264 insertions(+), 22 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,3 +1,5 @@ -shrt +/shrt shrt.conf shrt.db +shrt-*.tar.gz +/shrt*/ diff --git a/Makefile b/Makefile @@ -1,15 +1,37 @@ -VERSION := 0.1.0-dev0 +.POSIX: + +include config.mk all: shrt go.mod: cmd/shrt/main.go *.go - go mod tidy - touch go.mod + ${GO} mod tidy + @touch go.mod shrt: go.mod - go build -ldflags "-X main.version=${VERSION}" ./cmd/shrt + ${GO} build -ldflags ${GO_LDFLAGS} ./cmd/shrt clean: rm -f shrt -.PHONY: all clean- \ No newline at end of file +install: shrt + install -Dm755 shrt ${DESTDIR}${PREFIX}/bin/shrt + install -Dm644 shrt.1 ${DESTDIR}${MANPATH}/man1/shrt.1 + install -Dm644 shrt.1 ${DESTDIR}${MANPATH}/man5/shrtfile.5 + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/shrt + rm -f ${DESTDIR}${MANPATH}/man1/shrt.1 + rm -f ${DESTDIR}${MANPATH}/man5/shrtfile.5 + +dist: + rm -rf shrt-${VERSION} + mkdir shrt-${VERSION} + cp -r ${DIST_SRC} shrt-${VERSION} + tar -cf - shrt-${VERSION} | gzip > shrt-${VERSION}.tar.gz + +distclean: + rm -rf shrt-${VERSION} + rm -f shrt-${VERSION}.tar.gz + +.PHONY: all clean install uninstall dist distclean diff --git a/README b/README @@ -7,4 +7,38 @@ putting all the vowels in "shrt" and enjoy yourself! Also handles go-get redirects. There's no UI to add a new shortlink at this time. Just edit the DB by -hand. +hand and kill -HUP the process. + +building +-------- + +$ make + +installation +------------ + +# make install + +using +----- + +Note that by default shrt expects its data files to be relative to the +current directory. + +$ shrt -h +usage: shrt [-hv] [-d dbpath] [-c cfgpath] [-l listenaddr] [init] +$ shrt init +server name: example.com +SCM type: git # or hg, etc.. used for go-get redirects +repo suffix (blank for none): .git # suffix to add to go-get repo URIs +redirect base url: https://git.example.com +bare redirect url: https://www.example.com +$ shrt -l localhost:8080 +2020/12/06 12:57:01 listening on localhost:8080 + +HTTPS is out-of-scope. Use a reverse-proxy with TLS termination. + +more documentation +------------------ + +$ man 1 shrt diff --git a/cmd/shrt/main.go b/cmd/shrt/main.go @@ -6,8 +6,11 @@ import ( "log" "net/http" "os" + "os/signal" "path" "strings" + "sync" + "syscall" goshrt "djmo.ch/go-shrt" ) @@ -23,6 +26,7 @@ const ( var ( arg0 = path.Base(os.Args[0]) + mux = sync.RWMutex{} shrt, cfg *goshrt.ShrtFile version string @@ -56,12 +60,6 @@ func handl(w http.ResponseWriter, req *http.Request) { w.Write([]byte("Request path not allowed\n")) return } - if strings.Contains(key, "/") { - log.Println("bad request: " + key) - w.WriteHeader(http.StatusForbidden) - w.Write([]byte("Request path not allowed\n")) - return - } if key == "robots.txt" { log.Println("incoming robot") @@ -71,14 +69,6 @@ func handl(w http.ResponseWriter, req *http.Request) { w.Write([]byte(resp)) return } - if key == "robots.txt" { - log.Println("incoming robot") - resp := "# Welcome to Shrt\n" - resp += "User-Agent: *\n" - resp += "Disallow:\n" - w.Write([]byte(resp)) - return - } if key == "" && cfg.Get("barerdr") != "" { log.Println("shortlink request for /") @@ -88,6 +78,8 @@ func handl(w http.ResponseWriter, req *http.Request) { return } + mux.RLock() + defer mux.RUnlock() if val := shrt.Get(key); val != "" { log.Println("shortlink request for", key) w.Header().Add("Location", val) @@ -171,6 +163,22 @@ func doInit(path string) { m.Write() } +func hangup(dbpath string) { + hup := make(chan os.Signal) + signal.Notify(hup, syscall.SIGHUP) + for { + <-hup + tmpShrt, err := goshrt.ReadShrtFile(dbpath) + if err != nil { + log.Printf("db error -- %s\n", err.Error()) + } else { + mux.Lock() + shrt = tmpShrt + mux.Unlock() + } + } +} + func main() { var err error if len(os.Args) > 4 { @@ -223,6 +231,8 @@ func main() { os.Exit(errShrtFile) } + go hangup(dbpath) + http.Handle("/", http.HandlerFunc(handl)) log.Println("listening on", listenaddr) log.Fatal(http.ListenAndServe(listenaddr, nil)) diff --git a/config.mk b/config.mk @@ -0,0 +1,9 @@ +PREFIX := /usr/local +MANPATH := ${PREFIX}/man + +SRC = cmd/shrt/main.go shrtfile.go +DIST_SRC = cmd shrtfile.go Makefile config.mk shrt.1 shrtfile.5 README LICENSE go.mod go.sum +VERSION := 0.1.0-dev0 + +GO_LDFLAGS := "-X main.version=${VERSION}" +GO := go diff --git a/shrt.1 b/shrt.1 @@ -0,0 +1,101 @@ +.\" See LICENSE file for copyright and license details +.Dd 2020-12-06 +.Dt SHRT 1 +.Os +.Sh NAME +.Nm shrt +.Nd URL shortener and go-get handler +.Sh SYNOPSIS +.Nm +.Op Fl hv +.Op Fl c Ar cfgpath +.Op Fl d Ar dbpath +.Op Fl l Ar listenaddr +.Op Ar init +.Sh DESCRIPTION +.Nm +is a URL shortener service (much like bit.ly without the trackers) +that also handles go-get requests. +The latter are a specific GET request query used by the Go programming +language toolchain to aid in the downloading of utilities and +libraries prior to build and installation. +.Pp +Upon invocation, +.Nm +does one of two things is done depending on the presence or absence +of the +.Ar init +argument. +If the +.Ar init +argument is present, a series of questions is asked, the responses +are recorded in a configuration file, and the program exits. +See +.Xr shrtfile 5 +for a description of the config file syntax and contents. +If the +.Ar init +argument is absent, +.Nm +reads the configuration and database files into memory, binds to +the port specified by the +.Fl l +flag, and begins serving requests. +.Pp +Shortlinks are recorded in the database, and any request path not +matching a shortlink is assumed to be a go-get request. +This is by design, but can result in specious redirects. +Additionally, subdirectory paths are not allowed. +.Pp +Shortlinks generate an HTTP 301 response. +Go-get requests generate an HTTP 200 response. +If configured, requests to the base path (i.e., "/") generate an +HTTP 302 response. +.Pp +The database file is human-readable and formatted according to +.Xr shrtfile 5 , +basically a key-value store. +In order to add a new shortlink to the database, simply edit the file. +After saving, send SIGHUP to a running server process to reload the +file (see +.Xr kill 1 ). +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl h +Print usage and exit. +.It Fl v +Print version and exit. +.It Fl c Ar cfgpath +Change the path to search for the configuration file (default: shrt.conf) +.It Fl d Ar dbpath +Change the path to search for the database (default: shrt.db) +.It Fl l Ar listenaddr +Change the address:port to bind to (default: +.El +.Sh EXAMPLES +Initialize the +.Nm +configuration file: +.Pp +.Dl $ shrt init +.Pp +Run the server on localhost, port 8080: +.Pp +.Dl $ shrt -l localhost:8080 +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr shrtfile 5 , +.Xr kill 1 +.br +RFC 7231: Hypertext Transfer Protocol (HTTP/1.1): Semantics and +Content +.br +.Lk https://golang.org/doc/articles/go_command.html?h=go+get "About the go command" +.Sh HISTORY +.Nm +began in November 2020. +.Sh AUTHORS +.Nm +was written by +.An Daniel Moch Aq Mt daniel@danielmoch.com . diff --git a/shrtfile.5 b/shrtfile.5 @@ -0,0 +1,65 @@ +.\" See LICENSE file for copyright and license details +.Dd 2020-12-06 +.Dt SHRTFILE 5 +.Os +.Sh NAME +.Nm shrtfile , +.Nm shrt.conf , +.Nm shrt.db +.Nd URL shortener data files +.Sh DESCRIPTION +Shrtfiles are simple key-value values used by the +.Xr shrt 1 +URL shortener. +The syntax of the file is human readable. +Each line represents a key-value pair. +The key is everything to the left of the first equals sign, and the +value is everything to the right. +Whitespace is trimmed from the beginning and end of both keys and +values. +.Pp +.Pa shrt.db +Is a shrtfile containing all of the shortlinks recognized by the server. +The key is the shortlink (i.e., the request path), and the value +is the redirect URL. +.Pp +.Pa shrt.conf +is a shrtfile with a specific set of keys: +.Bl -tag -width Ds -compact +.It srvname +This is the FQDN of the +.Xr shrt 1 +server instance. +By convention URL shorteners keep this as short as possible (think +bit.ly, or something similar). +.It scmtype +The scmtype defines what SCM type to insert into go-get responses +(e.g., git, hg). +.It suffix +The suffix is appended to the redirect URL for go-get requests. +Use this if, for instance, you're redirecting to a Git server that +uses the .git extension for all of its clone URLs. +.It rdrname +The rdrname is the full, base URL to use for go-get redirects. +The formula for these redirects is {rdrname}/{request path}{suffix}. +.It barerdr +The barerdr is the redirect URL for requests to the base path (i.e., +"/"). +.El +.Sh FILES +.Bl -tag -width Ds -compact +.It Pa shrt.conf +.It Pa shrt.db +.El +.Sh EXAMPLES +The following file contents would suffice for +.Pa shrt.conf : +.Bd -literal +srvname = example.com +scmtype = git +suffix = .git +rdrname = https://git.example.com +barerdr = https://www.example.com +.Ed +.Sh SEE ALSO +.Xr shrt 1