gohttpserver/main.go

218 lines
6.2 KiB
Go
Raw Permalink Normal View History

2016-07-21 11:03:45 +02:00
package main
2016-07-21 11:13:40 +02:00
import (
2016-07-31 04:19:32 +02:00
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
2018-09-25 08:35:41 +02:00
"net"
"net/http"
2016-07-27 17:26:52 +02:00
"net/url"
"os"
2016-07-31 04:19:32 +02:00
"runtime"
"strconv"
2016-07-24 11:25:38 +02:00
"strings"
2016-07-31 04:19:32 +02:00
"text/template"
2016-07-22 14:20:17 +02:00
2016-07-21 11:13:40 +02:00
"github.com/alecthomas/kingpin"
2017-12-15 03:07:56 +01:00
accesslog "github.com/codeskyblue/go-accesslog"
"github.com/go-yaml/yaml"
2016-07-24 11:25:38 +02:00
"github.com/goji/httpauth"
2016-07-27 13:31:51 +02:00
"github.com/gorilla/handlers"
2018-09-14 15:03:27 +02:00
_ "github.com/shurcooL/vfsgen"
2016-07-21 11:13:40 +02:00
)
type Configure struct {
2020-12-04 21:44:03 +01:00
Conf *os.File `yaml:"-"`
Addr string `yaml:"addr"`
Port int `yaml:"port"`
Root string `yaml:"root"`
HTTPAuth string `yaml:"httpauth"`
Cert string `yaml:"cert"`
Key string `yaml:"key"`
Cors bool `yaml:"cors"`
Theme string `yaml:"theme"`
XHeaders bool `yaml:"xheaders"`
Upload bool `yaml:"upload"`
Delete bool `yaml:"delete"`
PlistProxy string `yaml:"plistproxy"`
Title string `yaml:"title"`
Debug bool `yaml:"debug"`
Auth struct {
2018-09-25 08:35:41 +02:00
Type string `yaml:"type"` // openid|http|github
2016-12-22 04:46:29 +01:00
OpenID string `yaml:"openid"`
HTTP string `yaml:"http"`
2018-09-25 08:35:41 +02:00
ID string `yaml:"id"` // for oauth2
Secret string `yaml:"secret"` // for oauth2
2016-12-22 04:46:29 +01:00
} `yaml:"auth"`
2016-07-21 11:13:40 +02:00
}
2017-12-15 03:07:56 +01:00
type httpLogger struct{}
2016-07-28 11:07:16 +02:00
2017-12-15 03:07:56 +01:00
func (l httpLogger) Log(record accesslog.LogRecord) {
2016-08-02 07:34:02 +02:00
log.Printf("%s - %s %d %s", record.Ip, record.Method, record.Status, record.Uri)
2016-07-28 11:07:16 +02:00
}
2016-07-28 09:24:41 +02:00
var (
2016-08-01 08:02:24 +02:00
defaultPlistProxy = "https://plistproxy.herokuapp.com/plist"
2018-09-25 08:35:41 +02:00
defaultOpenID = "https://login.netease.com/openid"
2016-08-01 08:02:24 +02:00
gcfg = Configure{}
2017-12-15 03:07:56 +01:00
logger = httpLogger{}
2016-07-31 04:19:32 +02:00
VERSION = "unknown"
BUILDTIME = "unknown time"
GITCOMMIT = "unknown git commit"
SITE = "https://github.com/codeskyblue/gohttpserver"
2016-07-28 09:24:41 +02:00
)
2016-07-21 11:03:45 +02:00
2016-07-31 04:19:32 +02:00
func versionMessage() string {
t := template.Must(template.New("version").Parse(`GoHTTPServer
Version: {{.Version}}
Go version: {{.GoVersion}}
OS/Arch: {{.OSArch}}
Git commit: {{.GitCommit}}
Built: {{.Built}}
Site: {{.Site}}`))
buf := bytes.NewBuffer(nil)
t.Execute(buf, map[string]interface{}{
"Version": VERSION,
"GoVersion": runtime.Version(),
"OSArch": runtime.GOOS + "/" + runtime.GOARCH,
"GitCommit": GITCOMMIT,
"Built": BUILDTIME,
"Site": SITE,
})
return buf.String()
}
func parseFlags() error {
// initial default conf
2016-08-02 10:06:44 +02:00
gcfg.Root = "./"
2018-09-25 08:35:41 +02:00
gcfg.Port = 8000
gcfg.Addr = ""
gcfg.Theme = "black"
gcfg.PlistProxy = defaultPlistProxy
2016-12-22 04:46:29 +01:00
gcfg.Auth.OpenID = defaultOpenID
gcfg.Title = "Go HTTP File Server"
2016-07-21 11:13:40 +02:00
kingpin.HelpFlag.Short('h')
2016-07-31 04:19:32 +02:00
kingpin.Version(versionMessage())
kingpin.Flag("conf", "config file path, yaml format").FileVar(&gcfg.Conf)
2016-08-02 10:06:44 +02:00
kingpin.Flag("root", "root directory, default ./").Short('r').StringVar(&gcfg.Root)
2018-09-25 08:35:41 +02:00
kingpin.Flag("port", "listen port, default 8000").IntVar(&gcfg.Port)
kingpin.Flag("addr", "listen address, eg 127.0.0.1:8000").Short('a').StringVar(&gcfg.Addr)
2016-07-24 10:51:57 +02:00
kingpin.Flag("cert", "tls cert.pem path").StringVar(&gcfg.Cert)
kingpin.Flag("key", "tls key.pem path").StringVar(&gcfg.Key)
2016-12-22 04:46:29 +01:00
kingpin.Flag("auth-type", "Auth type <http|openid>").StringVar(&gcfg.Auth.Type)
kingpin.Flag("auth-http", "HTTP basic auth (ex: user:pass)").StringVar(&gcfg.Auth.HTTP)
kingpin.Flag("auth-openid", "OpenID auth identity url").StringVar(&gcfg.Auth.OpenID)
kingpin.Flag("theme", "web theme, one of <black|green>").StringVar(&gcfg.Theme)
2016-08-01 08:02:24 +02:00
kingpin.Flag("upload", "enable upload support").BoolVar(&gcfg.Upload)
2017-05-17 08:59:32 +02:00
kingpin.Flag("delete", "enable delete support").BoolVar(&gcfg.Delete)
2016-08-01 08:50:24 +02:00
kingpin.Flag("xheaders", "used when behide nginx").BoolVar(&gcfg.XHeaders)
kingpin.Flag("cors", "enable cross-site HTTP request").BoolVar(&gcfg.Cors)
kingpin.Flag("debug", "enable debug mode").BoolVar(&gcfg.Debug)
kingpin.Flag("plistproxy", "plist proxy when server is not https").Short('p').StringVar(&gcfg.PlistProxy)
kingpin.Flag("title", "server title").StringVar(&gcfg.Title)
kingpin.Parse() // first parse conf
if gcfg.Conf != nil {
defer func() {
kingpin.Parse() // command line priority high than conf
}()
ymlData, err := ioutil.ReadAll(gcfg.Conf)
if err != nil {
return err
}
return yaml.Unmarshal(ymlData, &gcfg)
}
return nil
}
2016-07-21 11:13:40 +02:00
func main() {
if err := parseFlags(); err != nil {
log.Fatal(err)
}
if gcfg.Debug {
data, _ := yaml.Marshal(gcfg)
fmt.Printf("--- config ---\n%s\n", string(data))
}
2016-12-22 07:42:24 +01:00
log.SetFlags(log.Lshortfile | log.LstdFlags)
2016-07-22 14:20:17 +02:00
2016-07-28 10:19:14 +02:00
ss := NewHTTPStaticServer(gcfg.Root)
2016-07-27 16:22:16 +02:00
ss.Theme = gcfg.Theme
ss.Title = gcfg.Title
ss.Upload = gcfg.Upload
2017-05-17 08:59:32 +02:00
ss.Delete = gcfg.Delete
2016-12-22 05:05:34 +01:00
ss.AuthType = gcfg.Auth.Type
2016-07-24 11:25:38 +02:00
if gcfg.PlistProxy != "" {
u, err := url.Parse(gcfg.PlistProxy)
if err != nil {
log.Fatal(err)
}
u.Scheme = "https"
ss.PlistProxy = u.String()
2016-07-28 09:24:41 +02:00
}
if ss.PlistProxy != "" {
log.Printf("plistproxy: %s", strconv.Quote(ss.PlistProxy))
}
2020-12-04 21:44:03 +01:00
2016-07-27 16:22:16 +02:00
var hdlr http.Handler = ss
2016-07-28 11:07:16 +02:00
2017-12-15 03:07:56 +01:00
hdlr = accesslog.NewLoggingHandler(hdlr, logger)
2016-07-28 11:07:16 +02:00
2016-07-24 11:25:38 +02:00
// HTTP Basic Authentication
2016-12-22 04:46:29 +01:00
userpass := strings.SplitN(gcfg.Auth.HTTP, ":", 2)
switch gcfg.Auth.Type {
case "http":
if len(userpass) == 2 {
user, pass := userpass[0], userpass[1]
hdlr = httpauth.SimpleBasicAuth(user, pass)(hdlr)
}
case "openid":
2018-09-25 08:35:41 +02:00
handleOpenID(gcfg.Auth.OpenID, false) // FIXME(ssx): set secure default to false
// case "github":
// handleOAuth2ID(gcfg.Auth.Type, gcfg.Auth.ID, gcfg.Auth.Secret) // FIXME(ssx): set secure default to false
2019-02-21 08:41:09 +01:00
case "oauth2-proxy":
handleOauth2()
2016-07-24 11:25:38 +02:00
}
2018-09-25 08:35:41 +02:00
2016-07-26 07:27:36 +02:00
// CORS
if gcfg.Cors {
2016-07-27 13:31:51 +02:00
hdlr = handlers.CORS()(hdlr)
}
2016-08-01 04:42:32 +02:00
if gcfg.XHeaders {
2016-07-27 13:31:51 +02:00
hdlr = handlers.ProxyHeaders(hdlr)
2016-07-26 07:27:36 +02:00
}
2016-07-27 16:22:16 +02:00
2016-07-24 11:25:38 +02:00
http.Handle("/", hdlr)
2018-09-17 15:01:50 +02:00
http.Handle("/-/assets/", http.StripPrefix("/-/assets/", http.FileServer(Assets)))
2016-07-31 04:19:32 +02:00
http.HandleFunc("/-/sysinfo", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
data, _ := json.Marshal(map[string]interface{}{
"version": VERSION,
})
w.Write(data)
})
2016-07-26 08:06:01 +02:00
2018-09-25 08:35:41 +02:00
if gcfg.Addr == "" {
gcfg.Addr = fmt.Sprintf(":%d", gcfg.Port)
}
2016-08-02 07:34:02 +02:00
if !strings.Contains(gcfg.Addr, ":") {
gcfg.Addr = ":" + gcfg.Addr
}
2018-09-25 08:35:41 +02:00
_, port, _ := net.SplitHostPort(gcfg.Addr)
log.Printf("listening on %s, local address http://%s:%s\n", strconv.Quote(gcfg.Addr), getLocalIP(), port)
2016-07-24 10:51:57 +02:00
var err error
if gcfg.Key != "" && gcfg.Cert != "" {
err = http.ListenAndServeTLS(gcfg.Addr, gcfg.Cert, gcfg.Key, nil)
} else {
err = http.ListenAndServe(gcfg.Addr, nil)
}
log.Fatal(err)
2016-07-21 11:03:45 +02:00
}