rewrite proxy part
parent
15d3e5d050
commit
4debfbd3b8
@ -1,145 +1,33 @@
|
|||||||
# vim: set ft=yaml:
|
# vim: set ft=yaml:
|
||||||
|
|
||||||
# when provide certficate file, server will listen https and enable http2
|
# when provide certficate file, server will listen https and enable http2
|
||||||
|
listen:
|
||||||
|
-
|
||||||
# http config
|
addr: 0.0.0.0
|
||||||
-
|
|
||||||
# listen address
|
|
||||||
host: 0.0.0.0
|
|
||||||
|
|
||||||
# listen port
|
|
||||||
port: 9001
|
port: 9001
|
||||||
|
certificates:
|
||||||
|
-
|
||||||
|
addr: 0.0.0.0
|
||||||
|
port: 9002
|
||||||
|
certificates:
|
||||||
|
-
|
||||||
|
certfile: /etc/letsencrypt/live/ratafee.nl/fullchain.pem
|
||||||
|
keyfile: /etc/letsencrypt/live/ratafee.nl/privkey.pem
|
||||||
|
|
||||||
# default document root
|
# virtual host support
|
||||||
|
vhost:
|
||||||
|
-
|
||||||
|
hostname: www.ratafee.nl
|
||||||
docroot: /srv/www
|
docroot: /srv/www
|
||||||
|
proxypass: http://nginx:80/
|
||||||
enableproxy: true
|
proxy:
|
||||||
enableauth: true
|
http1-proxy: false
|
||||||
passwdfile: ./passwdfile
|
http2-proxy: true
|
||||||
realm: example.com
|
# trust the follow domains as local virtual host
|
||||||
|
# when http2 proxy enabled
|
||||||
# default host's url rule
|
localdomains:
|
||||||
# urlrules:
|
- localhost
|
||||||
# -
|
- localdomain
|
||||||
# urlprefix: /a
|
- 127.0.0.1
|
||||||
# type: alias
|
- ratafee.nl
|
||||||
# target:
|
- 98.142.138.194
|
||||||
# type: dir
|
|
||||||
# path: /home/user1/a
|
|
||||||
# -
|
|
||||||
# urlprefix: /b/a.txt
|
|
||||||
# type: alias
|
|
||||||
# target:
|
|
||||||
# type: file
|
|
||||||
# path: /home/user1/a/b/a.txt
|
|
||||||
|
|
||||||
# virtual host config
|
|
||||||
# vhost:
|
|
||||||
# - &example1_www
|
|
||||||
# hostname: www.example1.com
|
|
||||||
# docroot: /var/www/html/
|
|
||||||
# # cert:
|
|
||||||
# # key:
|
|
||||||
#
|
|
||||||
# # url rule for www.example.com
|
|
||||||
# urlrules:
|
|
||||||
# -
|
|
||||||
# # url start with /APIv1/ forward to uwsg socket
|
|
||||||
# urlprefix: /APIv1/
|
|
||||||
# type: uwsgi
|
|
||||||
# target:
|
|
||||||
# type: unix
|
|
||||||
# path: /run/uwsgi/APIv1.sock
|
|
||||||
# -
|
|
||||||
# # run php script on /phpmyadmin/ subdirectory
|
|
||||||
# urlprefix: /phpmyadmin/
|
|
||||||
# type: fastcgi
|
|
||||||
# target:
|
|
||||||
# type: unix
|
|
||||||
# path: /var/run/php-fpm/www.sock
|
|
||||||
# -
|
|
||||||
# # pass php to fastcgi socket
|
|
||||||
# urlprefix: \.php$|\.php/.*
|
|
||||||
# isregex: true
|
|
||||||
# type: fastcgi
|
|
||||||
# target:
|
|
||||||
# type: unix
|
|
||||||
# path: /var/run/php-fpm/www.sock
|
|
||||||
# -
|
|
||||||
# # run php script on other location
|
|
||||||
# urlprefix: /a/
|
|
||||||
# docroot: /home/user/php
|
|
||||||
# type: fastcgi
|
|
||||||
# target:
|
|
||||||
# type: unix
|
|
||||||
# path: /var/run/php-fpm/www.sock
|
|
||||||
# -
|
|
||||||
# # url start with /proxy/ reverse proxy for http://10.10.1.1/
|
|
||||||
# # this act as reverse proxy
|
|
||||||
# urlprefix: /proxy/
|
|
||||||
# type: reverse
|
|
||||||
# target:
|
|
||||||
# type: http
|
|
||||||
# host: 10.10.1.1
|
|
||||||
# port: 8080
|
|
||||||
# path: /
|
|
||||||
# - &example1
|
|
||||||
# <<: *example1_www
|
|
||||||
# hostname: example1.com
|
|
||||||
#
|
|
||||||
# - &example_www
|
|
||||||
# hostname: www.example.com
|
|
||||||
# docroot: /var/www/example
|
|
||||||
# urlrules:
|
|
||||||
# -
|
|
||||||
# urlprefix: /APIv2
|
|
||||||
# type: uwsgi
|
|
||||||
# target:
|
|
||||||
# type: unix
|
|
||||||
# path: /run/uwsgi/APIv2.sock
|
|
||||||
# - &example
|
|
||||||
# <<: *example_www
|
|
||||||
# hostname: example.com
|
|
||||||
#
|
|
||||||
# - &example_bbs
|
|
||||||
# hostname: bbs.example.com
|
|
||||||
# docroot: /var/www/example_bbs/
|
|
||||||
# urlrules:
|
|
||||||
# -
|
|
||||||
# #urlprefix: \.php$|\.php\/.*
|
|
||||||
# #isregex: true
|
|
||||||
#
|
|
||||||
# urlprefix: /
|
|
||||||
# type: fastcgi
|
|
||||||
# target:
|
|
||||||
# type: unix
|
|
||||||
# path: /var/run/php-fpm/www.sock
|
|
||||||
#
|
|
||||||
# https config
|
|
||||||
#-
|
|
||||||
# host: 0.0.0.0
|
|
||||||
# port: 9002
|
|
||||||
# docroot: /srv/www
|
|
||||||
# enableproxy: false
|
|
||||||
# vhost:
|
|
||||||
# -
|
|
||||||
# <<: *example1
|
|
||||||
# cert: /home/user1/cert/example1.com.crt
|
|
||||||
# key: /home/user1/cert/example1.com.key
|
|
||||||
# -
|
|
||||||
# <<: *example1_www
|
|
||||||
# cert: /home/user1/cert/example1.com.crt
|
|
||||||
# key: /home/user1/cert/example1.com.key
|
|
||||||
# -
|
|
||||||
# <<: *example_www
|
|
||||||
# cert: /etc/letsencrypt/live/example.com/fullchain.pem
|
|
||||||
# key: /etc/letsencrypt/live/example.com/privkey.pem
|
|
||||||
# -
|
|
||||||
# <<: *example
|
|
||||||
# cert: /etc/letsencrypt/live/example.com/fullchain.pem
|
|
||||||
# key: /etc/letsencrypt/live/example.com/privkey.pem
|
|
||||||
# -
|
|
||||||
# <<: *example_bbs
|
|
||||||
# cert: /etc/letsencrypt/live/bbs.example.com/fullchain.pem
|
|
||||||
# key: /etc/letsencrypt/live/bbs.example.com/privkey.pem
|
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
proxyproto "github.com/pires/go-proxyproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type listener struct {
|
||||||
|
net.Listener
|
||||||
|
}
|
||||||
|
|
||||||
|
type conn struct {
|
||||||
|
net.Conn
|
||||||
|
headerDone bool
|
||||||
|
r *bufio.Reader
|
||||||
|
proxy *proxyproto.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *listener) Accept() (net.Conn, error) {
|
||||||
|
c, err := l.Listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &conn{Conn: c}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) Read(buf []byte) (int, error) {
|
||||||
|
var err error
|
||||||
|
if !c.headerDone {
|
||||||
|
c.r = bufio.NewReader(c.Conn)
|
||||||
|
c.proxy, err = proxyproto.Read(c.r)
|
||||||
|
if err != nil && err != proxyproto.ErrNoProxyProtocol {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
c.headerDone = true
|
||||||
|
return c.r.Read(buf)
|
||||||
|
}
|
||||||
|
return c.r.Read(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) RemoteAddr() net.Addr {
|
||||||
|
if c.proxy == nil {
|
||||||
|
return c.Conn.RemoteAddr()
|
||||||
|
}
|
||||||
|
return &net.TCPAddr{
|
||||||
|
IP: c.proxy.SourceAddress,
|
||||||
|
Port: int(c.proxy.SourcePort)}
|
||||||
|
}
|
@ -1,90 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type digestPwFile struct {
|
|
||||||
path string
|
|
||||||
entry []pwEntry
|
|
||||||
mtime time.Time
|
|
||||||
mu *sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
type pwEntry struct {
|
|
||||||
user string
|
|
||||||
realm string
|
|
||||||
hashPw string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDigestSecret(f string) (*digestPwFile, error) {
|
|
||||||
a := &digestPwFile{path: f, mu: new(sync.Mutex)}
|
|
||||||
if err := a.loadFile(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
go a.tryReload()
|
|
||||||
return a, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (df *digestPwFile) tryReload() {
|
|
||||||
for {
|
|
||||||
time.Sleep(10 * time.Second)
|
|
||||||
fi, _ := os.Stat(df.path)
|
|
||||||
t1 := fi.ModTime()
|
|
||||||
if t1 != df.mtime {
|
|
||||||
df.loadFile()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (df *digestPwFile) loadFile() error {
|
|
||||||
df.mu.Lock()
|
|
||||||
defer df.mu.Unlock()
|
|
||||||
|
|
||||||
fp, err := os.Open(df.path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer fp.Close()
|
|
||||||
|
|
||||||
entry := []pwEntry{}
|
|
||||||
|
|
||||||
r := bufio.NewReader(fp)
|
|
||||||
for {
|
|
||||||
line, err := r.ReadString('\n')
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
line1 := strings.Trim(line, " \r\n")
|
|
||||||
if line1 == "" || line1[0] == '#' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fields := strings.SplitN(line1, ":", 3)
|
|
||||||
entry = append(entry, pwEntry{fields[0], fields[1], fields[2]})
|
|
||||||
}
|
|
||||||
|
|
||||||
df.entry = entry
|
|
||||||
fi, _ := os.Stat(df.path)
|
|
||||||
df.mtime = fi.ModTime()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (df *digestPwFile) getPw(user, realm string) string {
|
|
||||||
df.mu.Lock()
|
|
||||||
defer df.mu.Unlock()
|
|
||||||
|
|
||||||
for i := range df.entry {
|
|
||||||
if df.entry[i].user == user && df.entry[i].realm == realm {
|
|
||||||
return df.entry[i].hashPw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
# format
|
|
||||||
# user:realm:hashed_passwd
|
|
||||||
#
|
|
||||||
# hashed_passwd = MD5(user:realm:plain_passwd)
|
|
||||||
|
|
||||||
#
|
|
||||||
# user "test", realm "example.com", password "test"
|
|
||||||
# MD5("test:example:test") = 3441b753b98a6dc702183c989e35970f
|
|
||||||
# the entry is
|
|
||||||
test:example.com:3441b753b98a6dc702183c989e35970f
|
|
@ -1,262 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
auth "github.com/fangdingjun/go-http-auth"
|
|
||||||
"github.com/fangdingjun/gofast"
|
|
||||||
loghandler "github.com/gorilla/handlers"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httputil"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"sync"
|
|
||||||
//"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type logwriter struct {
|
|
||||||
w io.Writer
|
|
||||||
l *sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lw *logwriter) Write(buf []byte) (int, error) {
|
|
||||||
lw.l.Lock()
|
|
||||||
defer lw.l.Unlock()
|
|
||||||
return lw.w.Write(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func initRouters(cfg conf) {
|
|
||||||
|
|
||||||
logout := os.Stdout
|
|
||||||
|
|
||||||
if logfile != "" {
|
|
||||||
fp, err := os.OpenFile(logfile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
} else {
|
|
||||||
logout = fp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
w := &logwriter{logout, new(sync.Mutex)}
|
|
||||||
|
|
||||||
for _, l := range cfg {
|
|
||||||
router := mux.NewRouter()
|
|
||||||
domains := []string{}
|
|
||||||
certs := []tls.Certificate{}
|
|
||||||
|
|
||||||
// initial virtual host
|
|
||||||
for _, h := range l.Vhost {
|
|
||||||
h2 := h.Hostname
|
|
||||||
if h1, _, err := net.SplitHostPort(h.Hostname); err == nil {
|
|
||||||
h2 = h1
|
|
||||||
}
|
|
||||||
domains = append(domains, h2)
|
|
||||||
if h.Cert != "" && h.Key != "" {
|
|
||||||
if cert, err := tls.LoadX509KeyPair(h.Cert, h.Key); err == nil {
|
|
||||||
certs = append(certs, cert)
|
|
||||||
} else {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r := router.Host(h2).Subrouter()
|
|
||||||
for _, rule := range h.URLRules {
|
|
||||||
switch rule.Type {
|
|
||||||
case "alias":
|
|
||||||
registerAliasHandler(rule, r)
|
|
||||||
case "uwsgi":
|
|
||||||
registerUwsgiHandler(rule, r)
|
|
||||||
case "fastcgi":
|
|
||||||
registerFastCGIHandler(rule, h.Docroot, r)
|
|
||||||
case "reverse":
|
|
||||||
registerHTTPHandler(rule, r)
|
|
||||||
default:
|
|
||||||
fmt.Printf("invalid type: %s\n", rule.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r.PathPrefix("/").Handler(http.FileServer(http.Dir(h.Docroot)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// default host config
|
|
||||||
for _, rule := range l.URLRules {
|
|
||||||
switch rule.Type {
|
|
||||||
case "alias":
|
|
||||||
registerAliasHandler(rule, router)
|
|
||||||
case "uwsgi":
|
|
||||||
registerUwsgiHandler(rule, router)
|
|
||||||
case "fastcgi":
|
|
||||||
docroot := l.Docroot
|
|
||||||
if rule.Docroot != "" {
|
|
||||||
docroot = rule.Docroot
|
|
||||||
}
|
|
||||||
registerFastCGIHandler(rule, docroot, router)
|
|
||||||
case "reverse":
|
|
||||||
registerHTTPHandler(rule, router)
|
|
||||||
default:
|
|
||||||
fmt.Printf("invalid type: %s\n", rule.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
router.PathPrefix("/").Handler(http.FileServer(http.Dir(l.Docroot)))
|
|
||||||
|
|
||||||
go func(l server) {
|
|
||||||
addr := fmt.Sprintf("%s:%d", l.Host, l.Port)
|
|
||||||
hdlr := &handler{
|
|
||||||
handler: router,
|
|
||||||
enableProxy: l.EnableProxy,
|
|
||||||
enableAuth: l.EnableAuth,
|
|
||||||
localDomains: domains,
|
|
||||||
}
|
|
||||||
|
|
||||||
if l.EnableAuth {
|
|
||||||
if l.PasswdFile == "" {
|
|
||||||
log.Fatal("passwdfile required")
|
|
||||||
}
|
|
||||||
du, err := newDigestSecret(l.PasswdFile)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
digestAuth := auth.NewDigestAuthenticator(l.Realm, du.getPw)
|
|
||||||
digestAuth.Headers = auth.ProxyHeaders
|
|
||||||
hdlr.authMethod = digestAuth
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(certs) > 0 {
|
|
||||||
tlsconfig := &tls.Config{
|
|
||||||
Certificates: certs,
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsconfig.BuildNameToCertificate()
|
|
||||||
|
|
||||||
srv := http.Server{
|
|
||||||
Addr: addr,
|
|
||||||
TLSConfig: tlsconfig,
|
|
||||||
Handler: loghandler.CombinedLoggingHandler(w, hdlr),
|
|
||||||
}
|
|
||||||
log.Printf("listen https on %s", addr)
|
|
||||||
if err := srv.ListenAndServeTLS("", ""); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
log.Printf("listen http on %s", addr)
|
|
||||||
if err := http.ListenAndServe(
|
|
||||||
addr,
|
|
||||||
loghandler.CombinedLoggingHandler(w, hdlr),
|
|
||||||
); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}(l)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func registerAliasHandler(r rule, router *mux.Router) {
|
|
||||||
switch r.Target.Type {
|
|
||||||
case "file":
|
|
||||||
registerFileHandler(r, router)
|
|
||||||
case "dir":
|
|
||||||
registerDirHandler(r, router)
|
|
||||||
default:
|
|
||||||
fmt.Printf("invalid type: %s, only file, dir allowed\n", r.Target.Type)
|
|
||||||
os.Exit(-1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func registerFileHandler(r rule, router *mux.Router) {
|
|
||||||
router.HandleFunc(r.URLPrefix,
|
|
||||||
func(w http.ResponseWriter, req *http.Request) {
|
|
||||||
http.ServeFile(w, req, r.Target.Path)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func registerDirHandler(r rule, router *mux.Router) {
|
|
||||||
p := strings.TrimRight(r.URLPrefix, "/")
|
|
||||||
router.PathPrefix(r.URLPrefix).Handler(
|
|
||||||
http.StripPrefix(p,
|
|
||||||
http.FileServer(http.Dir(r.Target.Path))))
|
|
||||||
}
|
|
||||||
|
|
||||||
func registerUwsgiHandler(r rule, router *mux.Router) {
|
|
||||||
var p string
|
|
||||||
switch r.Target.Type {
|
|
||||||
case "unix":
|
|
||||||
p = r.Target.Path
|
|
||||||
case "tcp":
|
|
||||||
p = fmt.Sprintf("%s:%d", r.Target.Host, r.Target.Port)
|
|
||||||
default:
|
|
||||||
fmt.Printf("invalid scheme: %s, only support unix, tcp", r.Target.Type)
|
|
||||||
os.Exit(-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.IsRegex {
|
|
||||||
m1 := myURLMatch{regexp.MustCompile(r.URLPrefix)}
|
|
||||||
u := NewUwsgi(r.Target.Type, p, "")
|
|
||||||
router.MatcherFunc(m1.match).Handler(u)
|
|
||||||
} else {
|
|
||||||
u := NewUwsgi(r.Target.Type, p, r.URLPrefix)
|
|
||||||
router.PathPrefix(r.URLPrefix).Handler(u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func registerFastCGIHandler(r rule, docroot string, router *mux.Router) {
|
|
||||||
var n, p string
|
|
||||||
switch r.Target.Type {
|
|
||||||
case "unix":
|
|
||||||
n = "unix"
|
|
||||||
p = r.Target.Path
|
|
||||||
case "tcp":
|
|
||||||
n = "tcp"
|
|
||||||
p = fmt.Sprintf("%s:%d", r.Target.Host, r.Target.Port)
|
|
||||||
default:
|
|
||||||
fmt.Printf("invalid scheme: %s, only support unix, tcp", r.Target.Type)
|
|
||||||
os.Exit(-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
u := gofast.NewHandler(gofast.NewPHPFS(docroot), n, p)
|
|
||||||
if r.IsRegex {
|
|
||||||
m1 := myURLMatch{regexp.MustCompile(r.URLPrefix)}
|
|
||||||
router.MatcherFunc(m1.match).Handler(u)
|
|
||||||
} else {
|
|
||||||
router.PathPrefix(r.URLPrefix).Handler(u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func registerHTTPHandler(r rule, router *mux.Router) {
|
|
||||||
var u http.Handler
|
|
||||||
var addr string
|
|
||||||
switch r.Target.Type {
|
|
||||||
case "unix":
|
|
||||||
addr = r.Target.Path
|
|
||||||
u = newProxy(addr, r.URLPrefix)
|
|
||||||
case "http":
|
|
||||||
addr = fmt.Sprintf("%s:%d", r.Target.Host, r.Target.Port)
|
|
||||||
u1 := &url.URL{
|
|
||||||
Scheme: "http",
|
|
||||||
Host: addr,
|
|
||||||
Path: r.Target.Path,
|
|
||||||
}
|
|
||||||
u = httputil.NewSingleHostReverseProxy(u1)
|
|
||||||
default:
|
|
||||||
fmt.Printf("invalid scheme: %s, only support unix, http", r.Target.Type)
|
|
||||||
os.Exit(-1)
|
|
||||||
}
|
|
||||||
p := strings.TrimRight(r.URLPrefix, "/")
|
|
||||||
router.PathPrefix(r.URLPrefix).Handler(
|
|
||||||
http.StripPrefix(p, u))
|
|
||||||
}
|
|
||||||
|
|
||||||
type myURLMatch struct {
|
|
||||||
re *regexp.Regexp
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m myURLMatch) match(r *http.Request, route *mux.RouteMatch) bool {
|
|
||||||
ret := m.re.MatchString(r.URL.Path)
|
|
||||||
return ret
|
|
||||||
}
|
|
@ -1,23 +1,142 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"flag"
|
"flag"
|
||||||
//"fmt"
|
"fmt"
|
||||||
"log"
|
"net"
|
||||||
//"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/fangdingjun/go-log"
|
||||||
|
"github.com/gorilla/handlers"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var logfile string
|
func initServer(c *conf) error {
|
||||||
|
|
||||||
|
mux := mux.NewRouter()
|
||||||
|
|
||||||
|
for _, vh := range c.Vhosts {
|
||||||
|
subroute := mux.Host(vh.Hostname)
|
||||||
|
subroute.PathPrefix("/").Handler(http.FileServer(http.Dir(vh.Docroot)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.Vhosts) > 0 {
|
||||||
|
mux.PathPrefix("/").Handler(http.FileServer(http.Dir(c.Vhosts[0].Docroot)))
|
||||||
|
} else {
|
||||||
|
mux.PathPrefix("/").Handler(http.FileServer(http.Dir("/var/www/html")))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, _l := range c.Listens {
|
||||||
|
var err error
|
||||||
|
certs := []tls.Certificate{}
|
||||||
|
tlsconfig := &tls.Config{}
|
||||||
|
for _, cert := range _l.Certificates {
|
||||||
|
if cert.CertFile != "" && cert.KeyFile != "" {
|
||||||
|
_cert, err := tls.LoadX509KeyPair(cert.CertFile, cert.KeyFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
certs = append(certs, _cert)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var h http.Handler
|
||||||
|
|
||||||
|
h = &handler{handler: mux, cfg: c}
|
||||||
|
h = handlers.CombinedLoggingHandler(&logout{}, h)
|
||||||
|
|
||||||
|
srv := &http.Server{
|
||||||
|
Addr: fmt.Sprintf("%s:%d", _l.Addr, _l.Port),
|
||||||
|
Handler: h,
|
||||||
|
}
|
||||||
|
|
||||||
|
var l net.Listener
|
||||||
|
|
||||||
|
l, err = net.Listen("tcp", srv.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
l = &listener{Listener: l}
|
||||||
|
|
||||||
|
if len(certs) > 0 {
|
||||||
|
tlsconfig.Certificates = certs
|
||||||
|
tlsconfig.BuildNameToCertificate()
|
||||||
|
srv.TLSConfig = tlsconfig
|
||||||
|
http2.ConfigureServer(srv, nil)
|
||||||
|
l = tls.NewListener(l, srv.TLSConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func(l net.Listener) {
|
||||||
|
defer l.Close()
|
||||||
|
err = srv.Serve(l)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln(err)
|
||||||
|
}
|
||||||
|
}(l)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type logout struct{}
|
||||||
|
|
||||||
|
func (l *logout) Write(buf []byte) (int, error) {
|
||||||
|
log.Debugf("%s", buf)
|
||||||
|
return len(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var configfile string
|
var configfile string
|
||||||
|
var loglevel string
|
||||||
|
var logfile string
|
||||||
|
var logFileCount int
|
||||||
|
var logFileSize int64
|
||||||
|
flag.StringVar(&logfile, "log_file", "", "log file, default stdout")
|
||||||
|
flag.IntVar(&logFileCount, "log_count", 10, "max count of log to keep")
|
||||||
|
flag.Int64Var(&logFileSize, "log_size", 10, "max log file size MB")
|
||||||
|
flag.StringVar(&loglevel, "log_level", "INFO",
|
||||||
|
"log level, values:\nOFF, FATAL, PANIC, ERROR, WARN, INFO, DEBUG")
|
||||||
flag.StringVar(&configfile, "c", "config.yaml", "config file")
|
flag.StringVar(&configfile, "c", "config.yaml", "config file")
|
||||||
flag.StringVar(&logfile, "log", "", "log file")
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
if logfile != "" {
|
||||||
|
log.Default.Out = &log.FixedSizeFileWriter{
|
||||||
|
MaxCount: logFileCount,
|
||||||
|
Name: logfile,
|
||||||
|
MaxSize: logFileSize * 1024 * 1024,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if loglevel != "" {
|
||||||
|
lv, err := log.ParseLevel(loglevel)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
log.Default.Level = lv
|
||||||
|
}
|
||||||
|
|
||||||
c, err := loadConfig(configfile)
|
c, err := loadConfig(configfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
initRouters(c)
|
|
||||||
select {}
|
log.Infof("%+v", c)
|
||||||
|
err = initServer(c)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := make(chan os.Signal, 2)
|
||||||
|
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
select {
|
||||||
|
case sig := <-ch:
|
||||||
|
log.Errorf("received signal %s, exit", sig)
|
||||||
|
}
|
||||||
|
log.Debug("exited.")
|
||||||
}
|
}
|
||||||
|
@ -1,107 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
//"fmt"
|
|
||||||
uwsgi "github.com/fangdingjun/go-uwsgi"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Uwsgi is a struct for uwsgi
|
|
||||||
type Uwsgi struct {
|
|
||||||
Passenger *uwsgi.Passenger
|
|
||||||
URLPrefix string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUwsgi create a new Uwsgi
|
|
||||||
func NewUwsgi(network, addr, urlPrefix string) *Uwsgi {
|
|
||||||
u := strings.TrimRight(urlPrefix, "/")
|
|
||||||
return &Uwsgi{
|
|
||||||
Passenger: &uwsgi.Passenger{
|
|
||||||
Net: network,
|
|
||||||
Addr: addr,
|
|
||||||
},
|
|
||||||
URLPrefix: u,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeHTTP implements http.Handler interface
|
|
||||||
func (u *Uwsgi) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
u.UwsgiPass(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UwsgiPass pass the request to uwsgi interface
|
|
||||||
func (u *Uwsgi) UwsgiPass(w http.ResponseWriter, r *http.Request) {
|
|
||||||
params := buildParams(r, u.URLPrefix)
|
|
||||||
u.Passenger.UwsgiPass(w, r, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildParams(req *http.Request, urlPrefix string) map[string][]string {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
header := make(map[string][]string)
|
|
||||||
|
|
||||||
if urlPrefix != "" {
|
|
||||||
header["SCRIPT_NAME"] = []string{urlPrefix}
|
|
||||||
p := strings.Replace(req.URL.Path, urlPrefix, "", 1)
|
|
||||||
header["PATH_INFO"] = []string{p}
|
|
||||||
} else {
|
|
||||||
header["PATH_INFO"] = []string{req.URL.Path}
|
|
||||||
}
|
|
||||||
|
|
||||||
//fmt.Printf("url: %s, scheme: %s\n", req.URL.String(), req.URL.Scheme)
|
|
||||||
|
|
||||||
scheme := "http"
|
|
||||||
if req.TLS != nil {
|
|
||||||
scheme = "https"
|
|
||||||
}
|
|
||||||
header["REQUEST_SCHEME"] = []string{scheme}
|
|
||||||
|
|
||||||
header["HTTPS"] = []string{"off"}
|
|
||||||
|
|
||||||
/* https */
|
|
||||||
if scheme == "https" {
|
|
||||||
header["HTTPS"] = []string{"on"}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* speicial port */
|
|
||||||
host, port, err := net.SplitHostPort(req.Host)
|
|
||||||
if err != nil {
|
|
||||||
host = req.Host
|
|
||||||
if scheme == "http" {
|
|
||||||
port = "80"
|
|
||||||
} else {
|
|
||||||
port = "443"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
header["SERVER_NAME"] = []string{host}
|
|
||||||
header["SERVER_PORT"] = []string{port}
|
|
||||||
|
|
||||||
host, port, err = net.SplitHostPort(req.RemoteAddr)
|
|
||||||
if err != nil {
|
|
||||||
host = req.RemoteAddr
|
|
||||||
port = "80"
|
|
||||||
}
|
|
||||||
header["REMOTE_PORT"] = []string{port}
|
|
||||||
header["REMOTE_ADDR"] = []string{host}
|
|
||||||
|
|
||||||
header["REQUEST_METHOD"] = []string{req.Method}
|
|
||||||
header["REQUEST_URI"] = []string{req.RequestURI}
|
|
||||||
header["CONTENT_LENGTH"] = []string{strconv.Itoa(int(req.ContentLength))}
|
|
||||||
header["SERVER_PROTOCOL"] = []string{req.Proto}
|
|
||||||
header["QUERY_STRING"] = []string{req.URL.RawQuery}
|
|
||||||
|
|
||||||
if ctype := req.Header.Get("Content-Type"); ctype != "" {
|
|
||||||
header["CONTENT_TYPE"] = []string{ctype}
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range req.Header {
|
|
||||||
k = "HTTP_" + strings.ToUpper(strings.Replace(k, "-", "_", -1))
|
|
||||||
if _, ok := header[k]; ok == false {
|
|
||||||
header[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return header
|
|
||||||
}
|
|
Loading…
Reference in New Issue