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"
|
2016-08-02 10:01:15 +02:00
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
2016-07-23 10:37:38 +02:00
|
|
|
"log"
|
2018-09-25 08:35:41 +02:00
|
|
|
"net"
|
2016-07-23 10:37:38 +02:00
|
|
|
"net/http"
|
2016-07-27 17:26:52 +02:00
|
|
|
"net/url"
|
2016-08-02 10:01:15 +02:00
|
|
|
"os"
|
2016-07-31 04:19:32 +02:00
|
|
|
"runtime"
|
2016-07-23 10:37:38 +02:00
|
|
|
"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"
|
2016-08-02 10:01:15 +02:00
|
|
|
"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 {
|
2016-08-02 10:01:15 +02:00
|
|
|
Conf *os.File `yaml:"-"`
|
|
|
|
Addr string `yaml:"addr"`
|
2018-09-25 08:35:41 +02:00
|
|
|
Port int `yaml:"port"`
|
2016-08-02 10:01:15 +02:00
|
|
|
Root string `yaml:"root"`
|
2016-12-21 08:47:03 +01:00
|
|
|
HTTPAuth string `yaml:"httpauth"`
|
2016-08-02 10:01:15 +02:00
|
|
|
Cert string `yaml:"cert"`
|
|
|
|
Key string `yaml:"key"`
|
|
|
|
Cors bool `yaml:"cors"`
|
|
|
|
Theme string `yaml:"theme"`
|
|
|
|
XHeaders bool `yaml:"xheaders"`
|
|
|
|
Upload bool `yaml:"upload"`
|
2017-05-17 08:59:32 +02:00
|
|
|
Delete bool `yaml:"delete"`
|
2016-08-02 10:01:15 +02:00
|
|
|
PlistProxy string `yaml:"plistproxy"`
|
|
|
|
Title string `yaml:"title"`
|
|
|
|
Debug bool `yaml:"debug"`
|
2018-09-25 08:35:41 +02:00
|
|
|
GoogleTrackerID string `yaml:"google-tracker-id"`
|
2016-12-22 04:46:29 +01:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2016-08-02 10:01:15 +02:00
|
|
|
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 = ""
|
2016-08-02 10:01:15 +02:00
|
|
|
gcfg.Theme = "black"
|
|
|
|
gcfg.PlistProxy = defaultPlistProxy
|
2016-12-22 04:46:29 +01:00
|
|
|
gcfg.Auth.OpenID = defaultOpenID
|
2018-09-25 08:35:41 +02:00
|
|
|
gcfg.GoogleTrackerID = "UA-81205425-2"
|
2016-08-02 10:01:15 +02:00
|
|
|
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())
|
2016-08-02 10:01:15 +02:00
|
|
|
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)
|
2016-08-02 10:01:15 +02:00
|
|
|
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)
|
2016-08-02 10:01:15 +02:00
|
|
|
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)
|
2018-09-25 08:35:41 +02:00
|
|
|
kingpin.Flag("google-tracker-id", "set to empty to disable it").StringVar(&gcfg.GoogleTrackerID)
|
2016-08-02 10:01:15 +02:00
|
|
|
|
|
|
|
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-23 10:37:38 +02:00
|
|
|
}
|
2016-07-21 11:13:40 +02:00
|
|
|
|
2016-07-23 10:37:38 +02:00
|
|
|
func main() {
|
2016-08-02 10:01:15 +02:00
|
|
|
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
|
2016-07-31 13:31:05 +02:00
|
|
|
ss.Title = gcfg.Title
|
2018-09-25 09:59:38 +02:00
|
|
|
ss.GoogleTrackerID = gcfg.GoogleTrackerID
|
2016-08-02 08:02:23 +02:00
|
|
|
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
|
|
|
|
2016-08-02 10:01:15 +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
|
|
|
}
|
2019-01-25 06:27:39 +01:00
|
|
|
if ss.PlistProxy != "" {
|
|
|
|
log.Printf("plistproxy: %s", strconv.Quote(ss.PlistProxy))
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|