add virtual host support

master
dingjun 8 years ago
parent 22ae45beb5
commit 8512438638

@ -5,19 +5,23 @@ import (
"io/ioutil"
)
type conf struct {
Listen []listen
Docroot string
URLRules []rule
LocalDomains []string
}
type conf []server
type listen struct {
type server struct {
Host string
Port int
Cert string
Key string
Docroot string
UrlRules []rule
EnableProxy bool
Vhost []vhost
}
type vhost struct {
Docroot string
Hostname string
Cert string
Key string
UrlRules []rule
}
type rule struct {
@ -34,7 +38,7 @@ type target struct {
Path string
}
func loadConfig(fn string) (*conf, error) {
func loadConfig(fn string) (conf, error) {
data, err := ioutil.ReadFile(fn)
if err != nil {
return nil, err
@ -45,5 +49,5 @@ func loadConfig(fn string) (*conf, error) {
return nil, err
}
return &c, nil
return c, nil
}

@ -1,84 +1,129 @@
# vim: set ft=yaml:
#
# server config file
# document root
docroot: /var/www/html
# when provide certficate file, server will listen https and enable http2
# used for http2 to determine local path request
# or proxy request
localdomains:
- www.simicloud.com
- localhost
- 127.0.0.1
# listener
listen:
-
# listen address
host: 0.0.0.0
# http config
-
# listen address
host: 0.0.0.0
# listen port
port: 80
# listen port
port: 9001
# enable proxy or not
enableproxy: false
-
host: 0.0.0.0
port: 443
# server certificate
cert: server.crt
# server private key
key: server.key
enableproxy: false
# default document root
docroot: /srv/www
# url rules
urlrules:
-
# alias map url to path
urlprefix: /robots.txt
enableproxy: false
# available type: alias, fastcgi, uwsgi, http
type: alias
# default host's url rule
#urlrules:
# -
# urlprefix: /a
# type: alias
# target:
# type: dir
# path: /home/user1/a
# -
# urlprefix: /b/a.txt
# type: alias
# target:
# type: file
# path: /home/user1/a/b/a.txt
# target
target:
# availabe target type for alias: file, dir
type: file
path: /home/aaa/robots.txt
-
# alias map url to path
urlprefix: /cc
type: alias
target:
type: dir
path: /home/cc
-
# pass to uwsgi server
urlprefix: /media
type: uwsgi
target:
# available target type for uwsgi: unix, tcp
type: unix
path: /path/to/media.sock
-
# pass all php script to fastcgi server
urlprefix: \.php$|\.php/
# virtual host config
vhost:
- &example1_www
hostname: www.example1.com
docroot: /var/www/html/
# cert:
# key:
# use regex to match the url
isregex: true
# 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
-
# route php script to fastCGI socket
urlprefix: /phpmyadmin/.*\.php$
type: fastcgi
target:
# available target type for fastcgi: unix, tcp
type: unix
path: /path/to/php.sock
-
urlprefix: /auth
# http resverse proxy
type: http
target:
# available http type for http: http, unix
type: http
host: 127.0.0.1
port: 8080
# set to true means urlprefix is regex expression
isregex: true
type: fastcgi
target:
type: unix
path: /var/run/php-fpm/www.sock
-
# url start with /proxy/ forward to http://10.10.1.1
# this act as reverse proxy
urlprefix: /proxy/
type: http
target:
type: http
host: 10.10.1.1
port: 8080
- &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

@ -1,6 +1,7 @@
package main
import (
"fmt"
"github.com/yookoala/gofast"
"log"
"net"
@ -35,6 +36,15 @@ func (f *FastCGI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// FastCGIPass pass the request to fastcgi socket
func (f *FastCGI) FastCGIPass(w http.ResponseWriter, r *http.Request) {
// make sure server not access the file out of document root
p1 := filepath.Clean(filepath.Join(f.DocRoot, r.URL.Path))
p2 := filepath.Clean(f.DocRoot)
if !strings.HasPrefix(p1, p2) {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "invalid url")
return
}
var scriptName, pathInfo, scriptFileName string
conn, err := net.Dial(f.Network, f.Addr)
@ -58,7 +68,7 @@ func (f *FastCGI) FastCGIPass(w http.ResponseWriter, r *http.Request) {
if len(p) < 2 {
if strings.HasSuffix(r.URL.Path, "/") {
// redirect to index.php
scriptName = ""
scriptName = filepath.Join(r.URL.Path, "index.php")
pathInfo = ""
scriptFileName = filepath.Join(f.DocRoot, urlPath, "index.php")
} else {
@ -75,6 +85,10 @@ func (f *FastCGI) FastCGIPass(w http.ResponseWriter, r *http.Request) {
req := client.NewRequest(r)
req.Params["DOCUMENT_URI"] = scriptName
req.Params["SCRIPT_NAME"] = scriptName
req.Params["PHP_SELF"] = scriptName
req.Params["DOCUMENT_ROOT"] = f.DocRoot
req.Params["PATH_INFO"] = pathInfo
req.Params["SCRIPT_FILENAME"] = scriptFileName

@ -11,6 +11,7 @@ import (
)
type handler struct {
handler http.Handler
enableProxy bool
localDomains []string
}
@ -25,12 +26,20 @@ var defaultTransport http.RoundTripper = &http.Transport{
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.ProtoMajor == 1 && r.RequestURI[0] == '/' {
http.DefaultServeMux.ServeHTTP(w, r)
if h.handler != nil {
h.handler.ServeHTTP(w, r)
} else {
http.DefaultServeMux.ServeHTTP(w, r)
}
return
}
if r.ProtoMajor == 2 && h.isLocalRequest(r) {
http.DefaultServeMux.ServeHTTP(w, r)
if h.handler != nil {
h.handler.ServeHTTP(w, r)
} else {
http.DefaultServeMux.ServeHTTP(w, r)
}
return
}

@ -1,37 +1,108 @@
package main
import (
"crypto/tls"
"fmt"
"github.com/gorilla/mux"
"net"
"net/http"
//"net/url"
"log"
"os"
"regexp"
//"path/filepath"
"strings"
)
func initRouters(cfg *conf) {
router := mux.NewRouter()
for _, r := range cfg.URLRules {
switch r.Type {
case "alias":
registerAliasHandler(r, router)
case "uwsgi":
registerUwsgiHandler(r, router)
case "fastcgi":
registerFastCGIHandler(r, cfg.Docroot, router)
case "http":
registerHTTPHandler(r, router)
default:
fmt.Printf("invalid type: %s\n", r.Type)
func initRouters(cfg conf) {
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 "http":
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":
registerFastCGIHandler(rule, l.Docroot, router)
case "http":
registerHTTPHandler(rule, router)
default:
fmt.Printf("invalid type: %s\n", rule.Type)
}
}
}
router.PathPrefix("/").Handler(http.FileServer(http.Dir(cfg.Docroot)))
router.PathPrefix("/").Handler(http.FileServer(http.Dir(l.Docroot)))
http.Handle("/", router)
go func(l server) {
addr := fmt.Sprintf("%s:%d", l.Host, l.Port)
hdlr := &handler{
handler: router,
enableProxy: l.EnableProxy,
localDomains: domains,
}
if len(certs) > 0 {
tlsconfig := &tls.Config{
Certificates: certs,
}
tlsconfig.BuildNameToCertificate()
srv := http.Server{
Addr: addr,
TLSConfig: tlsconfig,
Handler: 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, hdlr); err != nil {
log.Fatal(err)
}
}
}(l)
}
}
func registerAliasHandler(r rule, router *mux.Router) {
@ -45,6 +116,7 @@ func registerAliasHandler(r rule, router *mux.Router) {
os.Exit(-1)
}
}
func registerFileHandler(r rule, router *mux.Router) {
router.HandleFunc(r.URLPrefix,
func(w http.ResponseWriter, req *http.Request) {

@ -2,29 +2,11 @@ package main
import (
"flag"
"fmt"
//"fmt"
"log"
"net/http"
//"net/http"
)
func initListeners(c *conf) {
for _, l := range c.Listen {
go func(l listen) {
addr := fmt.Sprintf("%s:%d", l.Host, l.Port)
h := &handler{enableProxy: l.EnableProxy, localDomains: c.LocalDomains}
if l.Cert != "" && l.Key != "" {
if err := http.ListenAndServeTLS(addr, l.Cert, l.Key, h); err != nil {
log.Fatal(err)
}
} else {
if err := http.ListenAndServe(addr, h); err != nil {
log.Fatal(err)
}
}
}(l)
}
}
func main() {
var configfile string
flag.StringVar(&configfile, "c", "config.yaml", "config file")
@ -34,6 +16,5 @@ func main() {
log.Fatal(err)
}
initRouters(c)
initListeners(c)
select {}
}

Loading…
Cancel
Save