Skip to content

Commit 94f3f0b

Browse files
committed
Initial commit
0 parents  commit 94f3f0b

9 files changed

+608
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/binaries/
2+
/release/
3+
/doc/screenshot.png

Makefile

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
VERSION = 1.0.0
2+
3+
APP := http-file-server
4+
PACKAGES := $(shell go list -f {{.Dir}} ./...)
5+
GOFILES := $(addsuffix /*.go,$(PACKAGES))
6+
GOFILES := $(wildcard $(GOFILES))
7+
8+
.PHONY: clean release README.md
9+
10+
clean:
11+
rm -rf binaries/
12+
rm -rf release/
13+
14+
# go get -u github.com/github/hub
15+
release: README.md zip
16+
git reset
17+
git add README.md
18+
git add Makefile
19+
git commit -m "Release $(VERSION)" || true
20+
git push
21+
hub release create $(VERSION) -m "$(VERSION)" -a release/$(APP)_$(VERSION)_osx_x86_64.tar.gz -a release/$(APP)_$(VERSION)_windows_x86_64.zip -a release/$(APP)_$(VERSION)_linux_x86_64.tar.gz -a release/$(APP)_$(VERSION)_osx_x86_32.tar.gz -a release/$(APP)_$(VERSION)_windows_x86_32.zip -a release/$(APP)_$(VERSION)_linux_x86_32.tar.gz -a release/$(APP)_$(VERSION)_linux_arm64.tar.gz
22+
23+
README.md:
24+
<README.template.md subst VERSION="$(VERSION)" APP="$(APP)" USAGE="$$(go get github.com/sgreben/$(APP); $(APP) -h 2>&1)" > README.md
25+
26+
zip: release/$(APP)_$(VERSION)_osx_x86_64.tar.gz release/$(APP)_$(VERSION)_windows_x86_64.zip release/$(APP)_$(VERSION)_linux_x86_64.tar.gz release/$(APP)_$(VERSION)_osx_x86_32.tar.gz release/$(APP)_$(VERSION)_windows_x86_32.zip release/$(APP)_$(VERSION)_linux_x86_32.tar.gz release/$(APP)_$(VERSION)_linux_arm64.tar.gz
27+
28+
binaries: binaries/osx_x86_64/$(APP) binaries/windows_x86_64/$(APP).exe binaries/linux_x86_64/$(APP) binaries/osx_x86_32/$(APP) binaries/windows_x86_32/$(APP).exe binaries/linux_x86_32/$(APP)
29+
30+
release/$(APP)_$(VERSION)_osx_x86_64.tar.gz: binaries/osx_x86_64/$(APP)
31+
mkdir -p release
32+
tar cfz release/$(APP)_$(VERSION)_osx_x86_64.tar.gz -C binaries/osx_x86_64 $(APP)
33+
34+
binaries/osx_x86_64/$(APP): $(GOFILES)
35+
GOOS=darwin GOARCH=amd64 go build -ldflags "-X main.version=$(VERSION)" -o binaries/osx_x86_64/$(APP) .
36+
37+
release/$(APP)_$(VERSION)_windows_x86_64.zip: binaries/windows_x86_64/$(APP).exe
38+
mkdir -p release
39+
cd ./binaries/windows_x86_64 && zip -r -D ../../release/$(APP)_$(VERSION)_windows_x86_64.zip $(APP).exe
40+
41+
binaries/windows_x86_64/$(APP).exe: $(GOFILES)
42+
GOOS=windows GOARCH=amd64 go build -ldflags "-X main.version=$(VERSION)" -o binaries/windows_x86_64/$(APP).exe .
43+
44+
release/$(APP)_$(VERSION)_linux_x86_64.tar.gz: binaries/linux_x86_64/$(APP)
45+
mkdir -p release
46+
tar cfz release/$(APP)_$(VERSION)_linux_x86_64.tar.gz -C binaries/linux_x86_64 $(APP)
47+
48+
binaries/linux_x86_64/$(APP): $(GOFILES)
49+
GOOS=linux GOARCH=amd64 go build -ldflags "-X main.version=$(VERSION)" -o binaries/linux_x86_64/$(APP) .
50+
51+
release/$(APP)_$(VERSION)_osx_x86_32.tar.gz: binaries/osx_x86_32/$(APP)
52+
mkdir -p release
53+
tar cfz release/$(APP)_$(VERSION)_osx_x86_32.tar.gz -C binaries/osx_x86_32 $(APP)
54+
55+
binaries/osx_x86_32/$(APP): $(GOFILES)
56+
GOOS=darwin GOARCH=386 go build -ldflags "-X main.version=$(VERSION)" -o binaries/osx_x86_32/$(APP) .
57+
58+
release/$(APP)_$(VERSION)_windows_x86_32.zip: binaries/windows_x86_32/$(APP).exe
59+
mkdir -p release
60+
cd ./binaries/windows_x86_32 && zip -r -D ../../release/$(APP)_$(VERSION)_windows_x86_32.zip $(APP).exe
61+
62+
binaries/windows_x86_32/$(APP).exe: $(GOFILES)
63+
GOOS=windows GOARCH=386 go build -ldflags "-X main.version=$(VERSION)" -o binaries/windows_x86_32/$(APP).exe .
64+
65+
release/$(APP)_$(VERSION)_linux_x86_32.tar.gz: binaries/linux_x86_32/$(APP)
66+
mkdir -p release
67+
tar cfz release/$(APP)_$(VERSION)_linux_x86_32.tar.gz -C binaries/linux_x86_32 $(APP)
68+
69+
binaries/linux_x86_32/$(APP): $(GOFILES)
70+
GOOS=linux GOARCH=386 go build -ldflags "-X main.version=$(VERSION)" -o binaries/linux_x86_32/$(APP) .
71+
72+
release/$(APP)_$(VERSION)_linux_arm64.tar.gz: binaries/linux_arm64/$(APP)
73+
mkdir -p release
74+
tar cfz release/$(APP)_$(VERSION)_linux_arm64.tar.gz -C binaries/linux_arm64 $(APP)
75+
76+
binaries/linux_arm64/$(APP): $(GOFILES)
77+
GOOS=linux GOARCH=arm64 go build -ldflags "-X main.version=$(VERSION)" -o binaries/linux_arm64/$(APP) .

README.md

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# http-file-server
2+
3+
`http-file-server` is a dependency-free HTTP file server.
4+
5+
![screenshot](doc/screenshot.gif)
6+
7+
## Contents
8+
9+
<!-- TOC -->
10+
11+
- [Contents](#contents)
12+
- [Examples](#examples)
13+
- [Get it](#get-it)
14+
- [Using `go get`](#using-go-get)
15+
- [Pre-built binary](#pre-built-binary)
16+
- [Use it](#use-it)
17+
18+
<!-- /TOC -->
19+
20+
21+
## Examples
22+
23+
```sh
24+
$ http-file-server /tmp
25+
2018/11/13 23:00:03.946051 serving local path "/tmp" on "/tmp/"
26+
2018/11/13 23:00:03.946087 redirecting to "/tmp/" from "/"
27+
2018/11/13 23:00:03.946093 http-file-server listening on ":8080"
28+
```
29+
30+
```sh
31+
$ http-file-server -p 1234 /1=/tmp /2=/var/tmp
32+
2018/11/13 23:01:44.502240 serving local path "/tmp" on "/1/"
33+
2018/11/13 23:01:44.502274 serving local path "/var/tmp" on "/2/"
34+
2018/11/13 23:01:44.502280 redirecting to "/1/" from "/"
35+
2018/11/13 23:01:44.502285 http-file-server listening on ":1234"
36+
```
37+
38+
```sh
39+
$ export PORT=9999
40+
$ http-file-server /abc/def/ghi=/tmp
41+
2018/11/13 23:05:52.291485 serving local path "/tmp" on "/abc/def/ghi/"
42+
2018/11/13 23:05:52.291606 redirecting to "/abc/def/ghi/" from "/"
43+
2018/11/13 23:05:52.291611 http-file-server listening on ":9999"
44+
```
45+
46+
## Get it
47+
48+
### Using `go get`
49+
50+
```sh
51+
go get -u github.com/sgreben/http-file-server
52+
```
53+
54+
### Pre-built binary
55+
56+
Or [download a binary](https://github.com/sgreben/http-file-server/releases/latest) from the releases page, or from the shell:
57+
58+
```sh
59+
# Linux
60+
curl -L https://github.com/sgreben/http-file-server/releases/download/1.0.0/http-file-server_1.0.0_linux_x86_64.tar.gz | tar xz
61+
62+
# OS X
63+
curl -L https://github.com/sgreben/http-file-server/releases/download/1.0.0/http-file-server_1.0.0_osx_x86_64.tar.gz | tar xz
64+
65+
# Windows
66+
curl -LO https://github.com/sgreben/http-file-server/releases/download/1.0.0/http-file-server_1.0.0_windows_x86_64.zip
67+
unzip versions_1.0.0_windows_x86_64.zip
68+
```
69+
70+
## Use it
71+
72+
```text
73+
http-file-server [OPTIONS] [[ROUTE=]PATH] [[ROUTE=]PATH...]
74+
```
75+
76+
```text
77+
Usage of http-file-server:
78+
-a string
79+
(alias for -addr) (default ":8080")
80+
-addr string
81+
address to listen on (environment variable "ADDR") (default ":8080")
82+
-p int
83+
(alias for -port)
84+
-port int
85+
port to listen on (overrides -addr port) (environment variable "PORT")
86+
-q (alias for -quiet)
87+
-quiet
88+
disable all log output (environment variable "QUIET")
89+
-r value
90+
(alias for -route)
91+
-route value
92+
a route definition ROUTE=PATH (ROUTE defaults to basename of PATH if omitted)
93+
```

README.template.md

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# http-file-server
2+
3+
`http-file-server` is a dependency-free HTTP file server.
4+
5+
![screenshot](doc/screenshot.gif)
6+
7+
## Contents
8+
9+
<!-- TOC -->
10+
11+
- [Contents](#contents)
12+
- [Examples](#examples)
13+
- [Get it](#get-it)
14+
- [Using `go get`](#using-go-get)
15+
- [Pre-built binary](#pre-built-binary)
16+
- [Use it](#use-it)
17+
18+
<!-- /TOC -->
19+
20+
21+
## Examples
22+
23+
```sh
24+
$ http-file-server /tmp
25+
2018/11/13 23:00:03.946051 serving local path "/tmp" on "/tmp/"
26+
2018/11/13 23:00:03.946087 redirecting to "/tmp/" from "/"
27+
2018/11/13 23:00:03.946093 http-file-server listening on ":8080"
28+
```
29+
30+
```sh
31+
$ http-file-server -p 1234 /1=/tmp /2=/var/tmp
32+
2018/11/13 23:01:44.502240 serving local path "/tmp" on "/1/"
33+
2018/11/13 23:01:44.502274 serving local path "/var/tmp" on "/2/"
34+
2018/11/13 23:01:44.502280 redirecting to "/1/" from "/"
35+
2018/11/13 23:01:44.502285 http-file-server listening on ":1234"
36+
```
37+
38+
```sh
39+
$ export PORT=9999
40+
$ http-file-server /abc/def/ghi=/tmp
41+
2018/11/13 23:05:52.291485 serving local path "/tmp" on "/abc/def/ghi/"
42+
2018/11/13 23:05:52.291606 redirecting to "/abc/def/ghi/" from "/"
43+
2018/11/13 23:05:52.291611 http-file-server listening on ":9999"
44+
```
45+
46+
## Get it
47+
48+
### Using `go get`
49+
50+
```sh
51+
go get -u github.com/sgreben/http-file-server
52+
```
53+
54+
### Pre-built binary
55+
56+
Or [download a binary](https://github.com/sgreben/http-file-server/releases/latest) from the releases page, or from the shell:
57+
58+
```sh
59+
# Linux
60+
curl -L https://github.com/sgreben/${APP}/releases/download/${VERSION}/${APP}_${VERSION}_linux_x86_64.tar.gz | tar xz
61+
62+
# OS X
63+
curl -L https://github.com/sgreben/${APP}/releases/download/${VERSION}/${APP}_${VERSION}_osx_x86_64.tar.gz | tar xz
64+
65+
# Windows
66+
curl -LO https://github.com/sgreben/${APP}/releases/download/${VERSION}/${APP}_${VERSION}_windows_x86_64.zip
67+
unzip versions_${VERSION}_windows_x86_64.zip
68+
```
69+
70+
## Use it
71+
72+
```text
73+
http-file-server [OPTIONS] [[ROUTE=]PATH] [[ROUTE=]PATH...]
74+
```
75+
76+
```text
77+
${USAGE}
78+
```

doc/screenshot.gif

81.8 KB
Loading

main.go

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"io/ioutil"
7+
"log"
8+
"net"
9+
"net/http"
10+
"os"
11+
"path/filepath"
12+
"strconv"
13+
)
14+
15+
const (
16+
defaultAddr = ":8080"
17+
addrEnvVarName = "ADDR"
18+
portEnvVarName = "PORT"
19+
quietEnvVarName = "QUIET"
20+
rootRoute = "/"
21+
)
22+
23+
var (
24+
addrFlag = os.Getenv(addrEnvVarName)
25+
portFlag64, _ = strconv.ParseInt(os.Getenv(portEnvVarName), 10, 64)
26+
portFlag = int(portFlag64)
27+
quietFlag bool
28+
routesFlag routes
29+
)
30+
31+
func init() {
32+
log.SetFlags(log.LUTC | log.Ldate | log.Ltime)
33+
log.SetOutput(os.Stderr)
34+
if addrFlag == "" {
35+
addrFlag = defaultAddr
36+
}
37+
flag.StringVar(&addrFlag, "addr", addrFlag, fmt.Sprintf("address to listen on (environment variable %q)", addrEnvVarName))
38+
flag.StringVar(&addrFlag, "a", addrFlag, "(alias for -addr)")
39+
flag.IntVar(&portFlag, "port", portFlag, fmt.Sprintf("port to listen on (overrides -addr port) (environment variable %q)", portEnvVarName))
40+
flag.IntVar(&portFlag, "p", portFlag, "(alias for -port)")
41+
flag.BoolVar(&quietFlag, "quiet", quietFlag, fmt.Sprintf("disable all log output (environment variable %q)", quietEnvVarName))
42+
flag.BoolVar(&quietFlag, "q", quietFlag, "(alias for -quiet)")
43+
flag.Var(&routesFlag, "route", routesFlag.help())
44+
flag.Var(&routesFlag, "r", "(alias for -route)")
45+
flag.Parse()
46+
if quietFlag {
47+
log.SetOutput(ioutil.Discard)
48+
}
49+
for i := 0; i < flag.NArg(); i++ {
50+
arg := flag.Arg(i)
51+
err := routesFlag.Set(arg)
52+
if err != nil {
53+
log.Fatalf("%q: %v", arg, err)
54+
}
55+
}
56+
}
57+
58+
func main() {
59+
addr, err := addr()
60+
if err != nil {
61+
log.Fatalf("address/port: %v", err)
62+
}
63+
err = server(addr, routesFlag)
64+
if err != nil {
65+
log.Fatalf("start server: %v", err)
66+
}
67+
}
68+
69+
func server(addr string, routes routes) error {
70+
mux := http.DefaultServeMux
71+
handlers := make(map[string]http.Handler)
72+
paths := make(map[string]string)
73+
74+
if len(routes.Values) == 0 {
75+
log.Printf("WARNING: no routes specified - every request will return %d (%s)", http.StatusNotFound, http.StatusText(http.StatusNotFound))
76+
http.HandleFunc(rootRoute, http.NotFound)
77+
}
78+
79+
for _, route := range routes.Values {
80+
handlers[route.Route] = &fileHandler{
81+
route: route.Route,
82+
path: route.Path,
83+
}
84+
paths[route.Route] = route.Path
85+
}
86+
87+
for route, path := range paths {
88+
mux.Handle(route, handlers[route])
89+
log.Printf("serving local path %q on %q", path, route)
90+
}
91+
92+
if _, ok := handlers[rootRoute]; !ok && len(routes.Values) > 0 {
93+
route := routes.Values[0].Route
94+
mux.Handle(rootRoute, http.RedirectHandler(route, http.StatusTemporaryRedirect))
95+
log.Printf("redirecting to %q from %q", route, rootRoute)
96+
}
97+
98+
binaryPath, _ := os.Executable()
99+
if binaryPath == "" {
100+
binaryPath = "server"
101+
}
102+
log.Printf("%s listening on %q", filepath.Base(binaryPath), addr)
103+
return http.ListenAndServe(addr, mux)
104+
}
105+
106+
func addr() (string, error) {
107+
portSet := portFlag != 0
108+
addrSet := addrFlag != ""
109+
switch {
110+
case portSet && addrSet:
111+
a, err := net.ResolveTCPAddr("tcp", addrFlag)
112+
if err != nil {
113+
return "", err
114+
}
115+
a.Port = portFlag
116+
return a.String(), nil
117+
case !portSet && addrSet:
118+
a, err := net.ResolveTCPAddr("tcp", addrFlag)
119+
if err != nil {
120+
return "", err
121+
}
122+
return a.String(), nil
123+
case portSet && !addrSet:
124+
return fmt.Sprintf(":%d", portFlag), nil
125+
case !portSet && !addrSet:
126+
fallthrough
127+
default:
128+
return defaultAddr, nil
129+
}
130+
}

0 commit comments

Comments
 (0)