Compare commits

...

8 commits

Author SHA1 Message Date
9bb92785ef Aggiorna README.md
Some checks failed
CI to Docker Hub / build (push) Has been cancelled
2025-04-16 16:54:27 +08:00
2dae4e04ca Aggiorna README.md
Some checks are pending
CI to Docker Hub / build (push) Waiting to run
2025-04-15 20:46:03 +08:00
Pierre Zemb
72ed8fa4a9
Merge pull request #53 from KonradHoeffner/patch-1
Create clickable link. Resolve #52.
2022-03-03 14:19:46 +01:00
Konrad Höffner
d82050d9bd
Create clickable link. Resolve #52. 2022-03-03 12:52:30 +01:00
Pierre Zemb
603738491f
Merge pull request #49 from deranjer/logger
adding logging
2022-02-14 07:41:34 +01:00
deranjer
7892ac7921 updating logRequest to be deprecated but accepted with warning messages about deprecation 2022-02-13 12:51:23 -05:00
deranjer
183e471a00 adding logging 2022-02-11 21:29:55 -05:00
Pierre Zemb
758675c3a9
Merge pull request #45 from PierreZ/dev/gh-actions
chore: CI, multi-arch docker buildx
2021-06-19 11:32:35 +02:00
5 changed files with 116 additions and 15 deletions

2
.gitignore vendored
View file

@ -1,2 +1,2 @@
vendor/ vendor/
goStatic goStatic*

View file

@ -1,6 +1,24 @@
# goStatic [![Docker Pulls](https://img.shields.io/docker/pulls/pierrezemb/gostatic.svg?style=plastic)](https://hub.docker.com/r/pierrezemb/gostatic/) [![Docker Build](https://img.shields.io/docker/build/pierrezemb/gostatic.svg?style=plastic)](https://hub.docker.com/r/pierrezemb/gostatic/) [![Build Status](https://travis-ci.org/PierreZ/goStatic.svg?branch=master)](https://travis-ci.org/PierreZ/goStatic) [![GoDoc](https://godoc.org/github.com/PierreZ/goStatic?status.svg)](https://godoc.org/github.com/PierreZ/goStatic) # goStatic [![Docker Pulls](https://img.shields.io/docker/pulls/pierrezemb/gostatic.svg?style=plastic)](https://hub.docker.com/r/pierrezemb/gostatic/) [![Docker Build](https://img.shields.io/docker/build/pierrezemb/gostatic.svg?style=plastic)](https://hub.docker.com/r/pierrezemb/gostatic/) [![Build Status](https://travis-ci.org/PierreZ/goStatic.svg?branch=master)](https://travis-ci.org/PierreZ/goStatic) [![GoDoc](https://godoc.org/github.com/PierreZ/goStatic?status.svg)](https://godoc.org/github.com/PierreZ/goStatic)
A really small, multi-arch, static web server for Docker A really small, multi-arch, static web server for Docker
# La mia applicazione
with docker
sudo docker run -d -p 8120:8043 -v /home/nvme/dockerdata/myweb:/srv/http --name myweb pierrezemb/gostatic
with docker compose or portainer
services:
gostatic:
ports:
- 8120:8043
volumes:
- /home/nvme/dockerdata/myweb:/srv/http
container_name: myweb
image: pierrezemb/gostatic
command: -set-basic-auth fabio:master66 #set auth login
## The goal ## The goal
My goal is to create to smallest docker container for my web static files. The advantage of Go is that you can generate a fully static binary, so that you don't need anything else. My goal is to create to smallest docker container for my web static files. The advantage of Go is that you can generate a fully static binary, so that you don't need anything else.

2
go.mod
View file

@ -1,3 +1,5 @@
module github.com/PierreZ/goStatic module github.com/PierreZ/goStatic
go 1.16 go 1.16
require github.com/rs/zerolog v1.26.1

32
go.sum Normal file
View file

@ -0,0 +1,32 @@
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc=
github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

77
main.go
View file

@ -9,11 +9,14 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"log"
"net/http" "net/http"
"os"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
) )
var ( var (
@ -28,7 +31,8 @@ var (
setBasicAuth = flag.String("set-basic-auth", "", "Define the basic auth. Form must be user:password") setBasicAuth = flag.String("set-basic-auth", "", "Define the basic auth. Form must be user:password")
defaultUsernameBasicAuth = flag.String("default-user-basic-auth", "gopher", "Define the user") defaultUsernameBasicAuth = flag.String("default-user-basic-auth", "gopher", "Define the user")
sizeRandom = flag.Int("password-length", 16, "Size of the randomized password") sizeRandom = flag.Int("password-length", 16, "Size of the randomized password")
logRequest = flag.Bool("enable-logging", false, "Enable log request") logLevel = flag.String("log-level", "info", "default: info - What level of logging to run, debug logs all requests (error, warn, info, debug)")
logRequest = flag.Bool("enable-logging", false, "Enable log request. NOTE: Deprecated, set log-level to debug to log all requests")
httpsPromote = flag.Bool("https-promote", false, "All HTTP requests should be redirected to HTTPS") httpsPromote = flag.Bool("https-promote", false, "All HTTP requests should be redirected to HTTPS")
headerConfigPath = flag.String("header-config-path", "/config/headerConfig.json", "Path to the config file for custom response headers") headerConfigPath = flag.String("header-config-path", "/config/headerConfig.json", "Path to the config file for custom response headers")
@ -36,6 +40,21 @@ var (
password string password string
) )
func setupLogger(logLevel string) {
switch logLevel {
case "error":
zerolog.SetGlobalLevel(zerolog.ErrorLevel)
case "warn":
zerolog.SetGlobalLevel(zerolog.WarnLevel)
case "info":
zerolog.SetGlobalLevel(zerolog.InfoLevel)
case "debug":
zerolog.SetGlobalLevel(zerolog.DebugLevel)
default:
zerolog.SetGlobalLevel(zerolog.InfoLevel)
}
}
func parseHeaderFlag(headerFlag string) (string, string) { func parseHeaderFlag(headerFlag string) (string, string) {
if len(headerFlag) == 0 { if len(headerFlag) == 0 {
return "", "" return "", ""
@ -72,16 +91,10 @@ func handleReq(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if *httpsPromote && r.Header.Get("X-Forwarded-Proto") == "http" { if *httpsPromote && r.Header.Get("X-Forwarded-Proto") == "http" {
http.Redirect(w, r, "https://"+r.Host+r.RequestURI, http.StatusMovedPermanently) http.Redirect(w, r, "https://"+r.Host+r.RequestURI, http.StatusMovedPermanently)
if *logRequest { log.Debug().Int("http_code", 301).Str("Method", r.Method).Str("Path", r.URL.Path).Msg("Request Redirected")
log.Println(301, r.Method, r.URL.Path)
}
return return
} }
log.Debug().Str("Method", r.Method).Str("Path", r.URL.Path).Msg("Request Handled")
if *logRequest {
log.Println(r.Method, r.URL.Path)
}
h.ServeHTTP(w, r) h.ServeHTTP(w, r)
}) })
} }
@ -90,16 +103,46 @@ func main() {
flag.Parse() flag.Parse()
// Setting up logging output before setting up level to print out deprecation warnings
// UNIX Time is faster and smaller than most timestamps
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
// Set a pretty console output
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
if *logRequest {
log.Warn().Msg("enable-logging is deprecated in favor of log-level")
}
//if log-level not default and enable-logging set to true, use log-level
if *logLevel != "info" && *logRequest {
log.Warn().Msg("log-level not 'info' and enable-logging set, using log-level")
*logRequest = false
}
//if log-level is info and we have enable-logging, set log-level to debug
if *logLevel == "info" && *logRequest {
log.Warn().Msg("since enable-logging is set, setting log-level to debug")
*logLevel = "debug"
}
//setting up the logger
setupLogger(*logLevel)
log.Debug().Str("Logging Level", zerolog.GlobalLevel().String()).Msg("Logger setup...")
// sanity check // sanity check
if len(*setBasicAuth) != 0 && !*basicAuth { if len(*setBasicAuth) != 0 && !*basicAuth {
log.Debug().Msg("Basic Auth Set")
*basicAuth = true *basicAuth = true
} }
port := ":" + strconv.FormatInt(int64(*portPtr), 10) port := ":" + strconv.FormatInt(int64(*portPtr), 10)
var fileSystem http.FileSystem = http.Dir(*basePath) var fileSystem http.FileSystem = http.Dir(*basePath)
log.Debug().Str("path", *basePath).Msg("File serve path set")
if *fallbackPath != "" { if *fallbackPath != "" {
log.Debug().Str("FallbackPath", *fallbackPath).Msg("Fallback path set")
fileSystem = fallback{ fileSystem = fallback{
defaultPath: *fallbackPath, defaultPath: *fallbackPath,
fs: fileSystem, fs: fileSystem,
@ -115,7 +158,7 @@ func main() {
} }
if *basicAuth { if *basicAuth {
log.Println("Enabling Basic Auth") log.Debug().Msg("Enabling Basic Auth")
if len(*setBasicAuth) != 0 { if len(*setBasicAuth) != 0 {
parseAuth(*setBasicAuth) parseAuth(*setBasicAuth)
} else { } else {
@ -135,6 +178,7 @@ func main() {
if len(header) > 0 && len(headerValue) > 0 { if len(header) > 0 && len(headerValue) > 0 {
fileServer := handler fileServer := handler
handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Debug().Str("URL", r.URL.Path).Str("header", header).Str("headerValue", headerValue).Msg("Extra Headers Handled")
w.Header().Set(header, headerValue) w.Header().Set(header, headerValue)
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
fileServer.ServeHTTP(w, r) fileServer.ServeHTTP(w, r)
@ -146,22 +190,27 @@ func main() {
gz.Reset(w) gz.Reset(w)
defer gz.Close() defer gz.Close()
fileServer.ServeHTTP(&gzipResponseWriter{ResponseWriter: w, Writer: gz}, r) fileServer.ServeHTTP(&gzipResponseWriter{ResponseWriter: w, Writer: gz}, r)
} }
}) })
} else { } else {
log.Println("appendHeader misconfigured; ignoring.") log.Warn().Msg("appendHeader misconfigured; ignoring.")
} }
} }
if *healthCheck { if *healthCheck {
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
log.Debug().Msg("Returning Service Health")
fmt.Fprintf(w, "Ok") fmt.Fprintf(w, "Ok")
}) })
} }
http.Handle(pathPrefix, handler) http.Handle(pathPrefix, handler)
log.Printf("Listening at 0.0.0.0%v %v...", port, pathPrefix) log.Info().Msgf("Listening at http://0.0.0.0%v %v...", port, pathPrefix)
log.Fatalln(http.ListenAndServe(port, nil)) if err := http.ListenAndServe(port, nil); err != nil && err != http.ErrServerClosed {
log.Fatal().Err(err).Msg("Server startup failed")
}
} }