123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643 |
- /*
- The MIT License (MIT)
- Copyright (c) 2019 winlin
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
- */
- /*
- This the main entrance of https-proxy, proxy to api or other http server.
- */
- package main
- import (
- "context"
- "crypto/tls"
- "flag"
- "fmt"
- oe "github.com/ossrs/go-oryx-lib/errors"
- oh "github.com/ossrs/go-oryx-lib/http"
- "github.com/ossrs/go-oryx-lib/https"
- ol "github.com/ossrs/go-oryx-lib/logger"
- "io/ioutil"
- "log"
- "net"
- "net/http"
- "net/http/httputil"
- "net/url"
- "os"
- "path"
- "strconv"
- "strings"
- "sync"
- )
- type Strings []string
- func (v *Strings) String() string {
- return fmt.Sprintf("strings [%v]", strings.Join(*v, ","))
- }
- func (v *Strings) Set(value string) error {
- *v = append(*v, value)
- return nil
- }
- func shouldProxyURL(srcPath, proxyPath string) bool {
- if !strings.HasSuffix(srcPath, "/") {
- // /api to /api/
- // /api.js to /api.js/
- // /api/100 to /api/100/
- srcPath += "/"
- }
- if !strings.HasSuffix(proxyPath, "/") {
- // /api/ to /api/
- // to match /api/ or /api/100
- // and not match /api.js/
- proxyPath += "/"
- }
- return strings.HasPrefix(srcPath, proxyPath)
- }
- // about x-real-ip and x-forwarded-for or
- // about X-Real-IP and X-Forwarded-For or
- // https://segmentfault.com/q/1010000002409659
- // https://distinctplace.com/2014/04/23/story-behind-x-forwarded-for-and-x-real-ip-headers/
- // @remark http proxy will set the X-Forwarded-For.
- func addProxyAddToHeader(remoteAddr, realIP string, fwd []string, header http.Header, omitForward bool) {
- rip, _, err := net.SplitHostPort(remoteAddr)
- if err != nil {
- return
- }
- if realIP != "" {
- header.Set("X-Real-IP", realIP)
- } else {
- header.Set("X-Real-IP", rip)
- }
- if !omitForward {
- header["X-Forwarded-For"] = fwd[:]
- header.Add("X-Forwarded-For", rip)
- }
- }
- func filterByPreHook(ctx context.Context, preHook *url.URL, req *http.Request) error {
- target := *preHook
- target.RawQuery = strings.Join([]string{target.RawQuery, req.URL.RawQuery}, "&")
- api := target.String()
- r, err := http.NewRequestWithContext(ctx, req.Method, api, nil)
- if err != nil {
- return err
- }
- // Add real ip and forwarded for to header.
- // We should append the forward for pass-by.
- addProxyAddToHeader(req.RemoteAddr, req.Header.Get("X-Real-IP"), req.Header["X-Forwarded-For"], r.Header, false)
- ol.Tf(ctx, "Pre-hook proxy addr req=%v, r=%v", req.Header, r.Header)
- r2, err := http.DefaultClient.Do(r)
- if err != nil {
- return err
- }
- defer r2.Body.Close()
- if r2.StatusCode != http.StatusOK {
- return fmt.Errorf("Pre-hook HTTP StatusCode=%v %v", r2.StatusCode, r2.Status)
- }
- b, err := ioutil.ReadAll(r2.Body)
- if err != nil {
- return err
- }
- ol.Tf(ctx, "Pre-hook %v url=%v, res=%v, headers=%v", req.Method, api, string(b), r.Header)
- return nil
- }
- func NewComplexProxy(ctx context.Context, proxyUrl, preHook *url.URL, originalRequest *http.Request) http.Handler {
- // Hook before proxy it.
- if preHook != nil {
- if err := filterByPreHook(ctx, preHook, originalRequest); err != nil {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- ol.Ef(ctx, "Pre-hook err %+v", err)
- http.Error(w, err.Error(), http.StatusInternalServerError)
- })
- }
- }
- // Start proxy it.
- proxy := &httputil.ReverseProxy{}
- proxyUrlQuery := proxyUrl.Query()
- // Create a proxy which attach a isolate logger.
- elogger := log.New(os.Stderr, fmt.Sprintf("%v ", originalRequest.RemoteAddr), log.LstdFlags)
- proxy.ErrorLog = elogger
- proxy.Director = func(r *http.Request) {
- // about the x-real-schema, we proxy to backend to identify the client schema.
- if rschema := r.Header.Get("X-Real-Schema"); rschema == "" {
- if r.TLS == nil {
- r.Header.Set("X-Real-Schema", "http")
- } else {
- r.Header.Set("X-Real-Schema", "https")
- }
- }
- // Add real ip and forwarded for to header.
- // We should omit the forward header, because the ReverseProxy will doit.
- addProxyAddToHeader(r.RemoteAddr, r.Header.Get("X-Real-IP"), r.Header["X-Forwarded-For"], r.Header, true)
- ol.Tf(ctx, "Proxy addr header %v", r.Header)
- r.URL.Scheme = proxyUrl.Scheme
- r.URL.Host = proxyUrl.Host
- // Trim the prefix path.
- if trimPrefix := proxyUrlQuery.Get("trimPrefix"); trimPrefix != "" {
- r.URL.Path = strings.TrimPrefix(r.URL.Path, trimPrefix)
- }
- // Aadd the prefix to path.
- if addPrefix := proxyUrlQuery.Get("addPrefix"); addPrefix != "" {
- r.URL.Path = addPrefix + r.URL.Path
- }
- // The original request.Host requested by the client.
- // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host
- if r.Header.Get("X-Forwarded-Host") == "" {
- r.Header.Set("X-Forwarded-Host", r.Host)
- }
- // Set the Host of client request to the upstream server's, to act as client
- // directly access the upstream server.
- if proxyUrlQuery.Get("modifyRequestHost") != "false" {
- r.Host = proxyUrl.Host
- }
- ra, url, rip := r.RemoteAddr, r.URL.String(), r.Header.Get("X-Real-Ip")
- ol.Tf(ctx, "proxy http rip=%v, addr=%v %v %v with headers %v", rip, ra, r.Method, url, r.Header)
- }
- proxy.ModifyResponse = func(w *http.Response) error {
- // We have already set the server, so remove the upstream one.
- if proxyUrlQuery.Get("keepUpsreamServer") != "true" {
- w.Header.Del("Server")
- }
- // We already added this header, it will cause chrome failed when duplicated.
- if w.Header.Get("Access-Control-Allow-Origin") != "" {
- w.Header.Del("Access-Control-Allow-Origin")
- }
- return nil
- }
- return proxy
- }
- func run(ctx context.Context) error {
- oh.Server = fmt.Sprintf("%v/%v", Signature(), Version())
- fmt.Println(oh.Server, "HTTP/HTTPS static server with API proxy.")
- var httpPorts Strings
- flag.Var(&httpPorts, "t", "http listen")
- flag.Var(&httpPorts, "http", "http listen at. 0 to disable http.")
- var httpsPorts Strings
- flag.Var(&httpsPorts, "s", "https listen")
- flag.Var(&httpsPorts, "https", "https listen at. 0 to disable https. 443 to serve. ")
- var httpsDomains string
- flag.StringVar(&httpsDomains, "d", "", "https the allow domains")
- flag.StringVar(&httpsDomains, "domains", "", "https the allow domains, empty to allow all. for example: ossrs.net,www.ossrs.net")
- var html string
- flag.StringVar(&html, "r", "./html", "the www web root")
- flag.StringVar(&html, "root", "./html", "the www web root. support relative dir to argv[0].")
- var cacheFile string
- flag.StringVar(&cacheFile, "e", "./letsencrypt.cache", "https the cache for letsencrypt")
- flag.StringVar(&cacheFile, "cache", "./letsencrypt.cache", "https the cache for letsencrypt. support relative dir to argv[0].")
- var useLetsEncrypt bool
- flag.BoolVar(&useLetsEncrypt, "l", false, "whether use letsencrypt CA")
- flag.BoolVar(&useLetsEncrypt, "lets", false, "whether use letsencrypt CA. self sign if not.")
- var ssKey string
- flag.StringVar(&ssKey, "k", "", "https self-sign key")
- flag.StringVar(&ssKey, "ssk", "", "https self-sign key")
- var ssCert string
- flag.StringVar(&ssCert, "c", "", `https self-sign cert`)
- flag.StringVar(&ssCert, "ssc", "", `https self-sign cert`)
- var oproxies Strings
- flag.Var(&oproxies, "p", "proxy ruler")
- flag.Var(&oproxies, "proxy", "one or more proxy the matched path to backend, for example, -proxy http://127.0.0.1:8888/api/webrtc")
- var oprehooks Strings
- flag.Var(&oprehooks, "pre-hook", "the pre-hook ruler, with request")
- var sdomains, skeys, scerts Strings
- flag.Var(&sdomains, "sdomain", "the SSL hostname")
- flag.Var(&skeys, "skey", "the SSL key for domain")
- flag.Var(&scerts, "scert", "the SSL cert for domain")
- var trimSlashLimit int
- var noRedirectIndex, trimLastSlash bool
- flag.BoolVar(&noRedirectIndex, "no-redirect-index", false, "Whether serve with index.html without redirect.")
- flag.BoolVar(&trimLastSlash, "trim-last-slash", false, "Whether trim last slash by HTTP redirect(302).")
- flag.IntVar(&trimSlashLimit, "trim-slash-limit", 0, "Only trim last slash when got enough directories.")
- flag.Usage = func() {
- fmt.Println(fmt.Sprintf("Usage: %v -t http -s https -d domains -r root -e cache -l lets -k ssk -c ssc -p proxy", os.Args[0]))
- fmt.Println(fmt.Sprintf(" "))
- fmt.Println(fmt.Sprintf("Options:"))
- fmt.Println(fmt.Sprintf(" -t, -http string"))
- fmt.Println(fmt.Sprintf(" Listen at port for HTTP server. Default: 0, disable HTTP."))
- fmt.Println(fmt.Sprintf(" -s, -https string"))
- fmt.Println(fmt.Sprintf(" Listen at port for HTTPS server. Default: 0, disable HTTPS."))
- fmt.Println(fmt.Sprintf(" -r, -root string"))
- fmt.Println(fmt.Sprintf(" The www root path. Supports relative to argv[0]=%v. Default: ./html", path.Dir(os.Args[0])))
- fmt.Println(fmt.Sprintf(" -no-redirect-index=bool"))
- fmt.Println(fmt.Sprintf(" Whether serve with index.html without redirect. Default: false"))
- fmt.Println(fmt.Sprintf(" -p, -proxy string"))
- fmt.Println(fmt.Sprintf(" Proxy path to backend. For example: http://127.0.0.1:8888/api/webrtc"))
- fmt.Println(fmt.Sprintf(" Proxy path to backend. For example: http://127.0.0.1:8888/api/webrtc?modifyRequestHost=false"))
- fmt.Println(fmt.Sprintf(" Proxy path to backend. For example: http://127.0.0.1:8888/api/webrtc?keepUpsreamServer=true"))
- fmt.Println(fmt.Sprintf(" Proxy path to backend. For example: http://127.0.0.1:8888/api/webrtc?trimPrefix=/ffmpeg"))
- fmt.Println(fmt.Sprintf(" Proxy path to backend. For example: http://127.0.0.1:8888/api/webrtc?addPrefix=/release"))
- fmt.Println(fmt.Sprintf(" -pre-hook string"))
- fmt.Println(fmt.Sprintf(" Pre-hook to backend, with request. For example: http://127.0.0.1:8888/api/stat"))
- fmt.Println(fmt.Sprintf("Options for HTTPS(letsencrypt cert):"))
- fmt.Println(fmt.Sprintf(" -l, -lets=bool"))
- fmt.Println(fmt.Sprintf(" Whether use letsencrypt CA. Default: false"))
- fmt.Println(fmt.Sprintf(" -e, -cache string"))
- fmt.Println(fmt.Sprintf(" The letsencrypt cache. Default: ./letsencrypt.cache"))
- fmt.Println(fmt.Sprintf(" -d, -domains string"))
- fmt.Println(fmt.Sprintf(" Set the validate HTTPS domain. For example: ossrs.net,www.ossrs.net"))
- fmt.Println(fmt.Sprintf("Options for HTTPS(file-based cert):"))
- fmt.Println(fmt.Sprintf(" -k, -ssk string"))
- fmt.Println(fmt.Sprintf(" The self-sign or validate file-based key file."))
- fmt.Println(fmt.Sprintf(" -c, -ssc string"))
- fmt.Println(fmt.Sprintf(" The self-sign or validate file-based cert file."))
- fmt.Println(fmt.Sprintf(" -sdomain string"))
- fmt.Println(fmt.Sprintf(" For multiple HTTPS site, the domain name. For example: ossrs.net"))
- fmt.Println(fmt.Sprintf(" -skey string"))
- fmt.Println(fmt.Sprintf(" For multiple HTTPS site, the key file."))
- fmt.Println(fmt.Sprintf(" -scert string"))
- fmt.Println(fmt.Sprintf(" For multiple HTTPS site, the cert file."))
- fmt.Println(fmt.Sprintf("For example:"))
- fmt.Println(fmt.Sprintf(" %v -t 8080 -s 9443 -r ./html", os.Args[0]))
- fmt.Println(fmt.Sprintf(" %v -t 8080 -s 9443 -r ./html -p http://ossrs.net:1985/api/v1/versions", os.Args[0]))
- fmt.Println(fmt.Sprintf("Generate cert for self-sign HTTPS:"))
- fmt.Println(fmt.Sprintf(" openssl genrsa -out server.key 2048"))
- fmt.Println(fmt.Sprintf(` openssl req -new -x509 -key server.key -out server.crt -days 365 -subj "/C=CN/ST=Beijing/L=Beijing/O=Me/OU=Me/CN=me.org"`))
- fmt.Println(fmt.Sprintf("For example:"))
- fmt.Println(fmt.Sprintf(" %v -s 9443 -r ./html -sdomain ossrs.net -skey ossrs.net.key -scert ossrs.net.pem", os.Args[0]))
- }
- flag.Parse()
- if useLetsEncrypt && len(httpsPorts) == 0 {
- return oe.Errorf("for letsencrypt, https=%v must be 0(disabled) or 443(enabled)", httpsPorts)
- }
- if len(httpPorts) == 0 && len(httpsPorts) == 0 {
- flag.Usage()
- os.Exit(-1)
- }
- // If trim last slash, we should enable no redirect index, to avoid infinitely redirect.
- if trimLastSlash {
- noRedirectIndex = true
- }
- fmt.Println(fmt.Sprintf("Config trimLastSlash=%v, trimSlashLimit=%v, noRedirectIndex=%v", trimLastSlash, trimSlashLimit, noRedirectIndex))
- var proxyUrls []*url.URL
- proxies := make(map[string]*url.URL)
- for _, oproxy := range []string(oproxies) {
- if oproxy == "" {
- return oe.Errorf("empty proxy in %v", oproxies)
- }
- proxyUrl, err := url.Parse(oproxy)
- if err != nil {
- return oe.Wrapf(err, "parse proxy %v", oproxy)
- }
- if _, ok := proxies[proxyUrl.Path]; ok {
- return oe.Errorf("proxy %v duplicated", proxyUrl.Path)
- }
- proxyUrls = append(proxyUrls, proxyUrl)
- proxies[proxyUrl.Path] = proxyUrl
- ol.Tf(ctx, "Proxy %v to %v", proxyUrl.Path, oproxy)
- }
- var preHookUrls []*url.URL
- preHooks := make(map[string]*url.URL)
- for _, oprehook := range []string(oprehooks) {
- if oprehook == "" {
- return oe.Errorf("empty pre-hook in %v", oprehooks)
- }
- preHookUrl, err := url.Parse(oprehook)
- if err != nil {
- return oe.Wrapf(err, "parse pre-hook %v", oprehook)
- }
- if _, ok := preHooks[preHookUrl.Path]; ok {
- return oe.Errorf("pre-hook %v duplicated", preHookUrl.Path)
- }
- preHookUrls = append(preHookUrls, preHookUrl)
- preHooks[preHookUrl.Path] = preHookUrl
- ol.Tf(ctx, "pre-hook %v to %v", preHookUrl.Path, oprehook)
- }
- if !path.IsAbs(cacheFile) && path.IsAbs(os.Args[0]) {
- cacheFile = path.Join(path.Dir(os.Args[0]), cacheFile)
- }
- if !path.IsAbs(html) && path.IsAbs(os.Args[0]) {
- html = path.Join(path.Dir(os.Args[0]), html)
- }
- serveFileNoRedirect := func (w http.ResponseWriter, r *http.Request, name string) {
- upath := path.Join(html, path.Clean(r.URL.Path))
- // Redirect without the last slash.
- if trimLastSlash && r.URL.Path != "/" && strings.HasSuffix(r.URL.Path, "/") {
- u := strings.TrimSuffix(r.URL.Path, "/")
- if r.URL.RawQuery != "" {
- u += "?" + r.URL.RawQuery
- }
- if strings.Count(u, "/") >= trimSlashLimit {
- http.Redirect(w, r, u, http.StatusFound)
- return
- }
- }
- // Append the index.html path if access a directory.
- if noRedirectIndex && !strings.Contains(path.Base(upath), ".") {
- if d, err := os.Stat(upath); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- } else if d.IsDir() {
- upath = path.Join(upath, "index.html")
- }
- }
- http.ServeFile(w, r, upath)
- }
- http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
- oh.SetHeader(w)
- if o := r.Header.Get("Origin"); len(o) > 0 {
- // SRS does not need cookie or credentials, so we disable CORS credentials, and use * for CORS origin,
- // headers, expose headers and methods.
- w.Header().Set("Access-Control-Allow-Origin", "*")
- // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
- w.Header().Set("Access-Control-Allow-Headers", "*")
- // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
- w.Header().Set("Access-Control-Allow-Methods", "*")
- // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers
- w.Header().Set("Access-Control-Expose-Headers", "*")
- // https://stackoverflow.com/a/24689738/17679565
- // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
- w.Header().Set("Access-Control-Allow-Credentials", "false")
- }
- // For matched OPTIONS, directly return without response.
- if r.Method == "OPTIONS" {
- return
- }
- if proxyUrls == nil {
- if r.URL.Path == "/httpx/v1/versions" {
- oh.WriteVersion(w, r, Version())
- return
- }
- serveFileNoRedirect(w, r, path.Join(html, path.Clean(r.URL.Path)))
- return
- }
- // Find pre-hook to serve with proxy.
- var preHook *url.URL
- for _, preHookUrl := range preHookUrls {
- if !shouldProxyURL(r.URL.Path, preHookUrl.Path) {
- continue
- }
- if p, ok := preHooks[preHookUrl.Path]; ok {
- preHook = p
- }
- }
- // Find proxy to serve it.
- for _, proxyUrl := range proxyUrls {
- if !shouldProxyURL(r.URL.Path, proxyUrl.Path) {
- continue
- }
- if proxy, ok := proxies[proxyUrl.Path]; ok {
- p := NewComplexProxy(ctx, proxy, preHook, r)
- p.ServeHTTP(w, r)
- return
- }
- }
- serveFileNoRedirect(w, r, path.Join(html, path.Clean(r.URL.Path)))
- })
- var protos []string
- if len(httpPorts) > 0 {
- protos = append(protos, fmt.Sprintf("http(:%v)", strings.Join(httpPorts, ",")))
- }
- for _, httpsPort := range httpsPorts {
- if httpsPort == "0" {
- continue
- }
- s := httpsDomains
- if httpsDomains == "" {
- s = "all domains"
- }
- if useLetsEncrypt {
- protos = append(protos, fmt.Sprintf("https(:%v, %v, %v)", httpsPort, s, cacheFile))
- } else {
- protos = append(protos, fmt.Sprintf("https(:%v)", httpsPort))
- }
- if useLetsEncrypt {
- protos = append(protos, "letsencrypt")
- } else if ssKey != "" {
- protos = append(protos, fmt.Sprintf("self-sign(%v, %v)", ssKey, ssCert))
- } else if len(sdomains) == 0 {
- return oe.New("no ssl config")
- }
- for i := 0; i < len(sdomains); i++ {
- sdomain, skey, scert := sdomains[i], skeys[i], scerts[i]
- if f, err := os.Open(scert); err != nil {
- return oe.Wrapf(err, "open cert %v for %v err %+v", scert, sdomain, err)
- } else {
- f.Close()
- }
- if f, err := os.Open(skey); err != nil {
- return oe.Wrapf(err, "open key %v for %v err %+v", skey, sdomain, err)
- } else {
- f.Close()
- }
- protos = append(protos, fmt.Sprintf("ssl(%v,%v,%v)", sdomain, skey, scert))
- }
- }
- ol.Tf(ctx, "%v html root at %v", strings.Join(protos, ", "), string(html))
- if len(httpsPorts) > 0 && !useLetsEncrypt && ssKey != "" {
- if f, err := os.Open(ssCert); err != nil {
- return oe.Wrapf(err, "open cert %v err %+v", ssCert, err)
- } else {
- f.Close()
- }
- if f, err := os.Open(ssKey); err != nil {
- return oe.Wrapf(err, "open key %v err %+v", ssKey, err)
- } else {
- f.Close()
- }
- }
- wg := sync.WaitGroup{}
- ctx, cancel := context.WithCancel(ctx)
- defer cancel()
- var httpServers []*http.Server
- for _, v := range httpPorts {
- httpPort, err := strconv.ParseInt(v, 10, 64)
- if err != nil {
- return oe.Wrapf(err, "parse %v", v)
- }
- wg.Add(1)
- go func(httpPort int) {
- defer wg.Done()
- ctx = ol.WithContext(ctx)
- if httpPort == 0 {
- ol.W(ctx, "http server disabled")
- return
- }
- defer cancel()
- hs := &http.Server{Addr: fmt.Sprintf(":%v", httpPort), Handler: nil}
- httpServers = append(httpServers, hs)
- ol.Tf(ctx, "http serve at %v", httpPort)
- if err := hs.ListenAndServe(); err != nil {
- ol.Ef(ctx, "http serve err %+v", err)
- return
- }
- ol.T("http server ok")
- }(int(httpPort))
- }
- for _, v := range httpsPorts {
- httpsPort, err := strconv.ParseInt(v, 10, 64)
- if err != nil {
- return oe.Wrapf(err, "parse %v", v)
- }
- wg.Add(1)
- go func() {
- defer wg.Done()
- ctx = ol.WithContext(ctx)
- if httpsPort == 0 {
- ol.W(ctx, "https server disabled")
- return
- }
- defer cancel()
- var err error
- var m https.Manager
- if useLetsEncrypt {
- var domains []string
- if httpsDomains != "" {
- domains = strings.Split(httpsDomains, ",")
- }
- if m, err = https.NewLetsencryptManager("", domains, cacheFile); err != nil {
- ol.Ef(ctx, "create letsencrypt manager err %+v", err)
- return
- }
- } else if ssKey != "" {
- if m, err = https.NewSelfSignManager(ssCert, ssKey); err != nil {
- ol.Ef(ctx, "create self-sign manager err %+v", err)
- return
- }
- } else if len(sdomains) > 0 {
- if m, err = NewCertsManager(sdomains, skeys, scerts); err != nil {
- ol.Ef(ctx, "create ssl managers err %+v", err)
- return
- }
- }
- hss := &http.Server{
- Addr: fmt.Sprintf(":%v", httpsPort),
- TLSConfig: &tls.Config{
- GetCertificate: m.GetCertificate,
- },
- }
- httpServers = append(httpServers, hss)
- ol.Tf(ctx, "https serve at %v", httpsPort)
- if err = hss.ListenAndServeTLS("", ""); err != nil {
- ol.Ef(ctx, "https serve err %+v", err)
- return
- }
- ol.T("https serve ok")
- }()
- }
- select {
- case <-ctx.Done():
- for _, server := range httpServers {
- server.Close()
- }
- }
- wg.Wait()
- return nil
- }
- func main() {
- ctx := ol.WithContext(context.Background())
- if err := run(ctx); err != nil {
- ol.Ef(ctx, "run err %+v", err)
- os.Exit(-1)
- }
- }
|