Compare commits
15 Commits
Author | SHA1 | Date |
---|---|---|
dingjun | c39fc8c107 | 5 years ago |
dingjun | c39cbf826a | 5 years ago |
dingjun | 4e6417da8b | 6 years ago |
dingjun | 341802505f | 6 years ago |
dingjun | ff7f97d9a2 | 6 years ago |
dingjun | 61fbd7ab0a | 6 years ago |
dingjun | 764434b372 | 6 years ago |
dingjun | 4cdd0c60f7 | 6 years ago |
dingjun | d0e5f8bb6a | 6 years ago |
dingjun | 5f176382ba | 6 years ago |
dingjun | 4ba30bdcfb | 6 years ago |
dingjun | 2973574537 | 6 years ago |
fangdingjun | 36da7093b2 | 6 years ago |
fangdingjun | 4734592a04 | 6 years ago |
fangdingjun | 4debfbd3b8 | 6 years ago |
@ -1,145 +1,33 @@
|
||||
# vim: set ft=yaml:
|
||||
|
||||
# when provide certficate file, server will listen https and enable http2
|
||||
|
||||
|
||||
# http config
|
||||
-
|
||||
# listen address
|
||||
host: 0.0.0.0
|
||||
|
||||
# listen port
|
||||
# when provide certficate file, server will listen https and enable http2
|
||||
listen:
|
||||
-
|
||||
addr: 0.0.0.0
|
||||
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
|
||||
|
||||
enableproxy: true
|
||||
enableauth: true
|
||||
passwdfile: ./passwdfile
|
||||
realm: example.com
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
proxypass: http://nginx:80/
|
||||
proxy:
|
||||
http1-proxy: false
|
||||
http2-proxy: true
|
||||
# trust the follow domains as local virtual host
|
||||
# when http2 proxy enabled
|
||||
localdomains:
|
||||
- localhost
|
||||
- localdomain
|
||||
- 127.0.0.1
|
||||
- ratafee.nl
|
||||
- 98.142.138.194
|
||||
|
@ -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 ""
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
module github.com/fangdingjun/gserver
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/fangdingjun/go-log v0.0.0-20190821073628-ae332053d6dc
|
||||
github.com/fangdingjun/protolistener v0.0.0-20190821093313-6d5d2138f296
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible
|
||||
github.com/gorilla/handlers v1.4.2
|
||||
github.com/gorilla/mux v1.7.3
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.2 // indirect
|
||||
)
|
@ -0,0 +1,31 @@
|
||||
github.com/fangdingjun/go-log v0.0.0-20190821073628-ae332053d6dc h1:Tdk7VsmsFo3d0NqHTy3SRoRnkduOxwXgR65gQsq8kXY=
|
||||
github.com/fangdingjun/go-log v0.0.0-20190821073628-ae332053d6dc/go.mod h1:oi7jbIScCbha6TbVzmrJP6igIHt+jcvvEgSJ7Ww1GkI=
|
||||
github.com/fangdingjun/protolistener v0.0.0-20190821093313-6d5d2138f296 h1:2c6agkdoPVSyvdJ0B+5DhOb1BQpso7a7zlBxXUnttmY=
|
||||
github.com/fangdingjun/protolistener v0.0.0-20190821093313-6d5d2138f296/go.mod h1:RoT81rjdN8gQ1w/z7NiFkxV6VzkT4NZ43XIt0lu8tcc=
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
||||
github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg=
|
||||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc h1:lNOt1SMsgHXTdpuGw+RpnJtzUcCb/oRKZP65pBy9pr8=
|
||||
github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc/go.mod h1:6/gX3+E/IYGa0wMORlSMla999awQFdbaeQCHjSMKIzY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a h1:Igim7XhdOpBnWPuYJ70XcNpq8q3BCACtVgNfoJxOV7g=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e h1:nFYrTHrdrAOpShe27kaFHjsqYSEQ0KWqdWLu3xuZJts=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
@ -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,72 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
//"bufio"
|
||||
//"fmt"
|
||||
"io"
|
||||
"log"
|
||||
//"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type proxy struct {
|
||||
transport http.RoundTripper
|
||||
addr string
|
||||
prefix string
|
||||
dialer *net.Dialer
|
||||
network string
|
||||
}
|
||||
|
||||
func newProxy(addr string, prefix string) *proxy {
|
||||
p := &proxy{
|
||||
addr: addr,
|
||||
prefix: prefix,
|
||||
dialer: &net.Dialer{Timeout: 2 * time.Second},
|
||||
}
|
||||
if addr[0] == '/' {
|
||||
p.network = "unix"
|
||||
} else {
|
||||
p.network = "tcp"
|
||||
}
|
||||
p.transport = &http.Transport{
|
||||
DialContext: p.dialContext,
|
||||
MaxIdleConns: 5,
|
||||
IdleConnTimeout: 30 * time.Second,
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *proxy) dialContext(ctx context.Context,
|
||||
network, addr string) (net.Conn, error) {
|
||||
return p.dialer.DialContext(ctx, p.network, p.addr)
|
||||
}
|
||||
|
||||
func (p *proxy) dial(network, addr string) (conn net.Conn, err error) {
|
||||
return p.dialer.Dial(p.network, p.addr)
|
||||
}
|
||||
|
||||
func (p *proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
host, _, _ := net.SplitHostPort(r.RemoteAddr)
|
||||
r.Header.Add("X-Forwarded-For", host)
|
||||
r.URL.Scheme = "http"
|
||||
r.URL.Host = r.Host
|
||||
resp, err := p.transport.RoundTrip(r)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
w.WriteHeader(http.StatusBadGateway)
|
||||
w.Write([]byte("<h1>502 Bad Gateway</h1>"))
|
||||
return
|
||||
}
|
||||
header := w.Header()
|
||||
for k, v := range resp.Header {
|
||||
for _, v1 := range v {
|
||||
header.Add(k, v1)
|
||||
}
|
||||
}
|
||||
w.WriteHeader(resp.StatusCode)
|
||||
io.Copy(w, resp.Body)
|
||||
resp.Body.Close()
|
||||
}
|
@ -0,0 +1 @@
|
||||
proxy_local*
|
@ -0,0 +1,9 @@
|
||||
proxy_local
|
||||
===========
|
||||
|
||||
accept http proxy request and forward to upstream proxy server via http2
|
||||
|
||||
usage
|
||||
====
|
||||
|
||||
use `./proxy_local -h` to see options
|
@ -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,154 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
//"fmt"
|
||||
"log"
|
||||
//"net/http"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/fangdingjun/go-log"
|
||||
"github.com/fangdingjun/protolistener"
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/net/trace"
|
||||
)
|
||||
|
||||
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)))
|
||||
}
|
||||
|
||||
mux.PathPrefix("/debug/").Handler(http.DefaultServeMux)
|
||||
|
||||
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,
|
||||
events: trace.NewEventLog("http", fmt.Sprintf("%s:%d", _l.Addr, _l.Port)),
|
||||
}
|
||||
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 = protolistener.New(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() {
|
||||
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(&logfile, "log", "", "log file")
|
||||
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)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
initRouters(c)
|
||||
select {}
|
||||
|
||||
log.Infof("%+v", c)
|
||||
err = initServer(c)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
trace.AuthRequest = func(r *http.Request) (bool, bool) {
|
||||
return true, true
|
||||
}
|
||||
|
||||
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