diff --git a/conf.go b/conf.go index a7c46c3..f72b7c9 100644 --- a/conf.go +++ b/conf.go @@ -1,48 +1,41 @@ package main import ( - "github.com/go-yaml/yaml" "io/ioutil" + + "github.com/go-yaml/yaml" ) -type conf []server - -type server struct { - Host string - Port int - Docroot string - URLRules []rule - EnableProxy bool - EnableAuth bool - PasswdFile string - Realm string - Vhost []vhost +type conf struct { + Listens []listen `yaml:"listen"` + Vhosts []vhost `yaml:"vhost"` + Proxy proxycfg `yaml:"proxy"` } -type vhost struct { - Docroot string - Hostname string - Cert string - Key string - URLRules []rule +type proxycfg struct { + HTTP1Proxy bool `yaml:"http1-proxy"` + HTTP2Proxy bool `yaml:"http2-proxy"` + LocalDomains []string `yaml:"localdomains"` } -type rule struct { - URLPrefix string - IsRegex bool - Docroot string - Type string - Target target +type listen struct { + Addr string `yaml:"addr"` + Port int16 `yaml:"port"` + Certificates []certificate `yaml:"certificates"` } -type target struct { - Type string - Host string - Port int - Path string +type certificate struct { + CertFile string `yaml:"certfile"` + KeyFile string `yaml:"keyfile"` +} + +type vhost struct { + Docroot string `yaml:"docroot"` + Hostname string `yaml:"hostname"` + ProxyPass string `yaml:"proxypass"` } -func loadConfig(fn string) (conf, error) { +func loadConfig(fn string) (*conf, error) { data, err := ioutil.ReadFile(fn) if err != nil { return nil, err @@ -53,5 +46,5 @@ func loadConfig(fn string) (conf, error) { return nil, err } - return c, nil + return &c, nil } diff --git a/config_example.yaml b/config_example.yaml index e60dc1f..6ac143f 100644 --- a/config_example.yaml +++ b/config_example.yaml @@ -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 diff --git a/conn.go b/conn.go new file mode 100644 index 0000000..8c74764 --- /dev/null +++ b/conn.go @@ -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)} +} diff --git a/digest_passwd_file.go b/digest_passwd_file.go deleted file mode 100644 index 3f06c40..0000000 --- a/digest_passwd_file.go +++ /dev/null @@ -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 "" -} diff --git a/handler.go b/handler.go index 821ff40..629ed3d 100644 --- a/handler.go +++ b/handler.go @@ -2,23 +2,20 @@ package main import ( "fmt" - auth "github.com/fangdingjun/go-http-auth" "io" - "log" "net" "net/http" "strings" "time" + + "github.com/fangdingjun/go-log" ) // handler process the proxy request first(if enabled) // and route the request to the registered http.Handler type handler struct { - handler http.Handler - enableProxy bool - enableAuth bool - authMethod *auth.DigestAuth - localDomains []string + handler http.Handler + cfg *conf } var defaultTransport http.RoundTripper = &http.Transport{ @@ -54,18 +51,16 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // proxy request - if !h.enableProxy { + if r.ProtoMajor == 1 && !h.cfg.Proxy.HTTP1Proxy { w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, "

404 Not Found

") return } - if h.enableAuth { - u, _ := h.authMethod.CheckAuth(r) - if u == "" { - h.authMethod.RequireAuth(w, r) - return - } + if r.ProtoMajor == 2 && !h.cfg.Proxy.HTTP2Proxy { + w.WriteHeader(http.StatusNotFound) + fmt.Fprintf(w, "

404 Not Found

") + return } if r.Method == http.MethodConnect { @@ -178,15 +173,15 @@ func (h *handler) handleCONNECT(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.(http.Flusher).Flush() - ch := make(chan int, 2) + ch := make(chan struct{}, 2) go func() { io.Copy(conn, r.Body) - ch <- 1 + ch <- struct{}{} }() go func() { io.Copy(flushWriter{w}, conn) - ch <- 1 + ch <- struct{}{} }() <-ch @@ -195,11 +190,11 @@ func (h *handler) handleCONNECT(w http.ResponseWriter, r *http.Request) { // isLocalRequest determine the http2 request is local path request // or the proxy request func (h *handler) isLocalRequest(r *http.Request) bool { - if !h.enableProxy { + if !h.cfg.Proxy.HTTP2Proxy { return true } - if len(h.localDomains) == 0 { + if len(h.cfg.Proxy.LocalDomains) == 0 { return true } @@ -208,7 +203,7 @@ func (h *handler) isLocalRequest(r *http.Request) bool { host = h1 } - for _, s := range h.localDomains { + for _, s := range h.cfg.Proxy.LocalDomains { if strings.HasSuffix(host, s) { return true } @@ -227,15 +222,15 @@ func pipeAndClose(r1, r2 io.ReadWriteCloser) { defer r1.Close() defer r2.Close() - ch := make(chan int, 2) + ch := make(chan struct{}, 2) go func() { io.Copy(r1, r2) - ch <- 1 + ch <- struct{}{} }() go func() { io.Copy(r2, r1) - ch <- 1 + ch <- struct{}{} }() <-ch diff --git a/passwdfile b/passwdfile deleted file mode 100644 index c2323a2..0000000 --- a/passwdfile +++ /dev/null @@ -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 diff --git a/proxy.go b/proxy.go index 8cb41d6..09a9e23 100644 --- a/proxy.go +++ b/proxy.go @@ -2,14 +2,12 @@ package main import ( "context" + "io" "net" "net/http" - //"bufio" - //"fmt" - "io" - "log" - //"strings" "time" + + "github.com/fangdingjun/go-log" ) type proxy struct { diff --git a/routers.go b/routers.go deleted file mode 100644 index 1f75f06..0000000 --- a/routers.go +++ /dev/null @@ -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 -} diff --git a/server.go b/server.go index 40c5bf2..5e411b3 100644 --- a/server.go +++ b/server.go @@ -1,23 +1,142 @@ 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/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() { 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) + } + + 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.") } diff --git a/uwsgi.go b/uwsgi.go deleted file mode 100644 index d2cf269..0000000 --- a/uwsgi.go +++ /dev/null @@ -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 -}