commit d01fea585574b78ffdfbfe5ae667d6612116aeb3
Author: Robert Russell <robertrussell.72001@gmail.com>
Date: Tue, 16 Jul 2024 22:19:26 -0700
Initial commit
Diffstat:
5 files changed, 80 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1 @@
+httpserve
diff --git a/go.mod b/go.mod
@@ -0,0 +1,5 @@
+module rr3.xyz/httpserve
+
+go 1.22.5
+
+require golang.org/x/sys v0.22.0 // indirect
diff --git a/go.sum b/go.sum
@@ -0,0 +1,2 @@
+golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
+golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
diff --git a/httpserve.1 b/httpserve.1
@@ -0,0 +1,15 @@
+.TH "HTTPSERVE" "1" "2024-07-16" "httpserve" "User Commands"
+.SH NAME
+httpserve \- Simple static HTTP server
+.SH SYNOPSIS
+.nf
+\fBtlsrp\fR \fBtcp\fR [\fIhost\fR]\fB:\fR[\fIport\fR] \fIroot\fR
+.fi
+.nf
+\fBtlsrp\fR \fBunix\fR \fIpath\fR \fIroot\fR
+.fi
+.SH DESCRIPTION
+httpserve serves static content on the indicated TCP or Unix address. For TCP,
+\fIhost\fR defaults to all available unicast and anycast IP addresses of the
+local system, and \fIport\fR defaults to being automatically chosen (and
+logged to stderr).
diff --git a/httpserve.go b/httpserve.go
@@ -0,0 +1,57 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "golang.org/x/sys/unix"
+ "log"
+ "net"
+ "net/http"
+ "os"
+ "os/signal"
+)
+
+func main() {
+ flag.Usage = func() {
+ format := `Usage: %[1]s tcp [HOST]:[PORT] ROOT_DIR
+ %[1]s unix PATH ROOT_DIR
+`
+ fmt.Fprintf(os.Stderr, format, os.Args[0])
+ os.Exit(1)
+ }
+
+ flag.Parse()
+ if flag.NArg() != 3 {
+ flag.Usage()
+ }
+ args := flag.Args()
+
+ network := args[0]
+ address := args[1]
+ root := args[2]
+
+ // We catch termination signals so that we can properly close the socket.
+ // We must register the signal channel before calling net.Listen to avoid
+ // a race condition.
+ sigs := make(chan os.Signal, 3)
+ signal.Notify(sigs, unix.SIGINT, unix.SIGQUIT, unix.SIGTERM)
+
+ listener, err := net.Listen(network, address)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer listener.Close()
+
+ serveErr := make(chan error, 1)
+ go func () {
+ log.Printf("serving %s on %s\n", root, listener.Addr())
+ serveErr <- http.Serve(listener, http.FileServer(http.Dir(root)))
+ }()
+
+ select {
+ case err := <-serveErr:
+ log.Print(err)
+ case sig := <-sigs:
+ log.Printf("received signal %s; terminating\n", sig)
+ }
+}