Setup HTTPS using autocert (#149)

* Add HTTPS WSS

* Update https

* HTTPS

* Add https

* Add HTTPS worker

* Add HTTPS

* add https

* Add pingserver to worker

* add https

* Add public domain whitelist

* Not fatal when there existing echo server
This commit is contained in:
giongto35 2020-01-03 04:37:26 +08:00 committed by GitHub
parent 4b9dc14c11
commit a1ac8f272a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 280 additions and 45 deletions

9
go.mod vendored
View file

@ -7,10 +7,19 @@ require (
github.com/gen2brain/x264-go v0.0.0-20180306035800-58f586137654
github.com/gofrs/uuid v3.2.0+incompatible
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/gorilla/mux v1.7.3
github.com/gorilla/websocket v1.4.0
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/pion/webrtc/v2 v2.1.16
github.com/prometheus/client_golang v1.1.0
github.com/spf13/pflag v1.0.3
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
golang.org/x/sys v0.0.0-20191218084908-4a24b4065292 // indirect
golang.org/x/tools v0.0.0-20191218191756-61f5e7d2990e // indirect
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
gopkg.in/hraban/opus.v2 v2.0.0-20180426093920-0f2e0b4fc6cd
)

14
go.sum vendored
View file

@ -51,6 +51,8 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@ -176,9 +178,12 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4=
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067 h1:KYGJGHOQy8oSi1fDlSpcZF0+juKwk/hEMv5SiwHogR0=
@ -191,6 +196,7 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -209,6 +215,8 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 h1:N66aaryRB3Ax92gH0v3hp1QYZ3zWWCCUR/j8Ifh45Ss=
golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
@ -218,6 +226,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -236,6 +245,8 @@ golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdO
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c h1:S/FtSvpNLtFBgjTqcKsRpsa6aVsI6iztaz1bQd9BJwE=
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191218084908-4a24b4065292 h1:Y8q0zsdcgAd+JU8VUA8p8Qv2YhuY9zevDG2ORt5qBUI=
golang.org/x/sys v0.0.0-20191218084908-4a24b4065292/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
@ -252,6 +263,9 @@ golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191218191756-61f5e7d2990e/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0 h1:9sdfJOzWlkqPltHAuzT2Cp+yrBeY1KRVYgms8soxMwM=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=

View file

@ -22,6 +22,7 @@ var StunTurnTemplate = `[{"urls":"stun:stun.l.google.com:19302"},{"urls":"stun:%
var WSWait = 20 * time.Second
var MatchWorkerRandom = false
var ProdEnv = "prod"
var StagingEnv = "staging"
const NumKeys = 10

View file

@ -6,16 +6,18 @@ import (
)
type Config struct {
Port int
URLPrefix string
DebugHost string
Port int
PublicDomain string
URLPrefix string
DebugHost string
MonitoringConfig monitoring.ServerMonitoringConfig
}
func NewDefaultConfig() Config {
return Config{
Port: 8800,
Port: 8800,
PublicDomain: "http://localhost:8000",
MonitoringConfig: monitoring.ServerMonitoringConfig{
Port: 6601,
@ -34,6 +36,7 @@ func (c *Config) AddFlags(fs *pflag.FlagSet) *Config {
fs.IntVarP(&c.MonitoringConfig.Port, "monitoring.port", "", c.MonitoringConfig.Port, "Monitoring server port")
fs.StringVarP(&c.MonitoringConfig.URLPrefix, "monitoring.prefix", "", c.MonitoringConfig.URLPrefix, "Monitoring server url prefix")
fs.StringVarP(&c.DebugHost, "debughost", "d", "", "Specify the server want to connect directly to debug")
fs.StringVarP(&c.PublicDomain, "domain", "n", c.PublicDomain, "Specify the server want to connect directly to debug")
return c
}

View file

@ -30,6 +30,9 @@ type Server struct {
workerClients map[string]*WorkerClient
}
const pingServerTemp = "https://%s.%s/echo"
const devPingServer = "http://localhost:9000/echo"
var upgrader = websocket.Upgrader{}
var errNotFound = errors.New("Not found")
@ -65,6 +68,16 @@ func (o *Server) GetWeb(w http.ResponseWriter, r *http.Request) {
tmpl.Execute(w, data)
}
// getPingServer returns the server for latency check of a zone. In latency check to find best worker step, we use this server to find the closest worker.
func (o *Server) getPingServer(zone string) string {
if *config.Mode == config.ProdEnv || *config.Mode == config.StagingEnv {
return fmt.Sprintf(pingServerTemp, zone, o.cfg.PublicDomain)
}
// If not Prod or Staging, return dev environment
return devPingServer
}
// WSO handles all connections from a new worker to overlord
func (o *Server) WSO(w http.ResponseWriter, r *http.Request) {
fmt.Println("Connected")
@ -82,8 +95,11 @@ func (o *Server) WSO(w http.ResponseWriter, r *http.Request) {
// Zone of the worker
zone := r.URL.Query().Get("zone")
pingServer := o.getPingServer(zone)
fmt.Printf("Is public: %v zone: %v\n", util.IsPublicIP(address), zone)
// In case worker and overlord in the same host
if !util.IsPublicIP(address) && *config.Mode == config.ProdEnv {
// Don't accept private IP for worker's address in prod mode
// However, if the worker in the same host with overlord, we can get public IP of worker
@ -95,7 +111,7 @@ func (o *Server) WSO(w http.ResponseWriter, r *http.Request) {
return
}
}
client := NewWorkerClient(c, serverID, address, fmt.Sprintf(config.StunTurnTemplate, address, address), zone)
client := NewWorkerClient(c, serverID, address, fmt.Sprintf(config.StunTurnTemplate, address, address), zone, pingServer)
o.workerClients[serverID] = client
defer o.cleanConnection(client, serverID)
@ -213,7 +229,7 @@ func (o *Server) getBestWorkerClient(client *BrowserClient, zone string) (*Worke
workerClients := o.getAvailableWorkers()
serverID, err := findBestServerFromBrowser(workerClients, client, zone)
serverID, err := o.findBestServerFromBrowser(workerClients, client, zone)
if err != nil {
log.Println(err)
return nil, err
@ -247,13 +263,13 @@ func (o *Server) getWorkerFromAddress(address string) *WorkerClient {
// findBestServerFromBrowser returns the best server for a session
// All workers addresses are sent to user and user will ping to get latency
func findBestServerFromBrowser(workerClients map[string]*WorkerClient, client *BrowserClient, zone string) (string, error) {
func (o *Server) findBestServerFromBrowser(workerClients map[string]*WorkerClient, client *BrowserClient, zone string) (string, error) {
// TODO: Find best Server by latency, currently return by ping
if len(workerClients) == 0 {
return "", errors.New("No server found")
}
latencies := getLatencyMapFromBrowser(workerClients, client)
latencies := o.getLatencyMapFromBrowser(workerClients, client)
log.Println("Latency map", latencies)
if len(latencies) == 0 {
@ -280,16 +296,15 @@ func findBestServerFromBrowser(workerClients map[string]*WorkerClient, client *B
}
// getLatencyMapFromBrowser get all latencies from worker to user
func getLatencyMapFromBrowser(workerClients map[string]*WorkerClient, client *BrowserClient) map[*WorkerClient]int64 {
func (o *Server) getLatencyMapFromBrowser(workerClients map[string]*WorkerClient, client *BrowserClient) map[*WorkerClient]int64 {
workersList := []*WorkerClient{}
addressList := []string{}
latencyMap := map[*WorkerClient]int64{}
// addressList is the list of worker addresses
addressList := []string{}
for _, workerClient := range workerClients {
workersList = append(workersList, workerClient)
addressList = append(addressList, workerClient.Address)
addressList = append(addressList, workerClient.PingServer)
}
// send this address to user and get back latency
@ -307,7 +322,7 @@ func getLatencyMapFromBrowser(workerClients map[string]*WorkerClient, client *Br
}
for _, workerClient := range workersList {
if latency, ok := respLatency[workerClient.Address]; ok {
if latency, ok := respLatency[workerClient.PingServer]; ok {
latencyMap[workerClient] = latency
}
}

View file

@ -2,13 +2,23 @@ package overlord
import (
"context"
"crypto/tls"
"fmt"
"log"
"net/http"
"time"
"github.com/giongto35/cloud-game/pkg/config"
"github.com/giongto35/cloud-game/pkg/monitoring"
"github.com/golang/glog"
"github.com/gorilla/mux"
"golang.org/x/crypto/acme"
"golang.org/x/crypto/acme/autocert"
)
const stagingLEURL = "https://acme-staging-v02.api.letsencrypt.org/directory"
type Overlord struct {
ctx context.Context
cfg Config
@ -45,24 +55,97 @@ func (o *Overlord) Shutdown() {
}
}
func makeServerFromMux(mux *http.ServeMux) *http.Server {
// set timeouts so that a slow or malicious client doesn't
// hold resources forever
return &http.Server{
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
IdleTimeout: 120 * time.Second,
Handler: mux,
}
}
func makeHTTPServer(server *Server) *http.Server {
r := mux.NewRouter()
r.HandleFunc("/", server.GetWeb)
r.HandleFunc("/ws", server.WS)
r.HandleFunc("/wso", server.WSO)
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("./web"))))
svmux := &http.ServeMux{}
svmux.Handle("/", r)
return makeServerFromMux(svmux)
}
func makeHTTPToHTTPSRedirectServer(server *Server) *http.Server {
handleRedirect := func(w http.ResponseWriter, r *http.Request) {
newURI := "https://" + r.Host + r.URL.String()
http.Redirect(w, r, newURI, http.StatusFound)
}
r := mux.NewRouter()
r.HandleFunc("/", handleRedirect)
r.HandleFunc("/ws", handleRedirect)
r.HandleFunc("/wso", handleRedirect)
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("./web"))))
svmux := &http.ServeMux{}
svmux.Handle("/", r)
return makeServerFromMux(svmux)
}
// initializeOverlord setup an overlord server
func (o *Overlord) initializeOverlord() {
overlord := NewServer(o.cfg)
http.HandleFunc("/", overlord.GetWeb)
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./web"))))
var certManager *autocert.Manager
var httpsSrv *http.Server
// browser facing port
go func() {
http.HandleFunc("/ws", overlord.WS)
}()
log.Println("Initializing Overlord Server")
if *config.Mode == config.ProdEnv || *config.Mode == config.StagingEnv {
var leurl string
if *config.Mode == config.StagingEnv {
leurl = stagingLEURL
} else {
leurl = acme.LetsEncryptURL
}
// worker facing port
http.HandleFunc("/wso", overlord.WSO)
log.Println("Listening at port: localhost:8000")
err := http.ListenAndServe(":8000", nil)
// Print err if overlord cannot launch
certManager = &autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist(o.cfg.PublicDomain),
Cache: autocert.DirCache("assets/cache"),
Client: &acme.Client{DirectoryURL: leurl},
}
httpsSrv = makeHTTPServer(overlord)
httpsSrv.Addr = ":443"
httpsSrv.TLSConfig = &tls.Config{GetCertificate: certManager.GetCertificate}
go func() {
fmt.Printf("Starting HTTPS server on %s\n", httpsSrv.Addr)
err := httpsSrv.ListenAndServeTLS("", "")
if err != nil {
log.Fatalf("httpsSrv.ListendAndServeTLS() failed with %s", err)
}
}()
}
var httpSrv *http.Server
if *config.Mode == config.ProdEnv || *config.Mode == config.StagingEnv {
httpSrv = makeHTTPToHTTPSRedirectServer(overlord)
} else {
httpSrv = makeHTTPServer(overlord)
}
if certManager != nil {
httpSrv.Handler = certManager.HTTPHandler(httpSrv.Handler)
}
httpSrv.Addr = ":8000"
err := httpSrv.ListenAndServe()
if err != nil {
log.Fatal(err)
log.Fatalf("httpSrv.ListenAndServe() failed with %s", err)
}
}

View file

@ -7,10 +7,14 @@ import (
"github.com/gorilla/websocket"
)
const pingServer = "%s://%s/echo"
type WorkerClient struct {
*cws.Client
ServerID string
Address string
ServerID string
Address string // ip address of worker
// public server used for ping check (Cannot use worker address because they are not publicly exposed)
PingServer string
StunTurnServer string
IsAvailable bool
Zone string
@ -57,10 +61,11 @@ func (o *Server) RouteWorker(workerClient *WorkerClient) {
}
// NewWorkerClient returns a client connecting to worker. This connection exchanges information between workers and server
func NewWorkerClient(c *websocket.Conn, serverID string, address string, stunturn string, zone string) *WorkerClient {
func NewWorkerClient(c *websocket.Conn, serverID string, address string, stunturn string, zone, pingServer string) *WorkerClient {
return &WorkerClient{
Client: cws.NewClient(c),
ServerID: serverID,
PingServer: pingServer,
Address: address,
StunTurnServer: stunturn,
IsAvailable: true,

View file

@ -1,12 +1,14 @@
package worker
import (
"crypto/tls"
"log"
"net/url"
"os"
"path"
"time"
"github.com/giongto35/cloud-game/pkg/config"
"github.com/giongto35/cloud-game/pkg/config/worker"
"github.com/giongto35/cloud-game/pkg/util"
@ -61,13 +63,13 @@ func (h *Handler) Run() {
for {
oClient, err := setupOverlordConnection(h.overlordHost, h.cfg.Zone)
if err != nil {
log.Println("Cannot connect to overlord. Retrying...")
log.Printf("Cannot connect to overlord. %v Retrying...", err)
time.Sleep(time.Second)
continue
}
h.oClient = oClient
log.Println("Connected to overlord successfully.")
log.Println("Connected to overlord successfully.", oClient, err)
go h.oClient.Heartbeat()
h.RouteOverlord()
h.oClient.Listen()
@ -76,28 +78,43 @@ func (h *Handler) Run() {
}
func setupOverlordConnection(ohost string, zone string) (*OverlordClient, error) {
var scheme string
if *config.Mode == config.ProdEnv || *config.Mode == config.StagingEnv {
scheme = "wss"
} else {
scheme = "ws"
}
overlordURL := url.URL{
Scheme: "ws",
Scheme: scheme,
Host: ohost,
Path: "/wso",
RawQuery: "zone=" + zone,
}
log.Println("Worker connecting to overlord:", overlordURL.String())
conn, err := createOverlordConnection(overlordURL.String())
conn, err := createOverlordConnection(&overlordURL)
if err != nil {
return nil, err
}
return NewOverlordClient(conn), nil
}
func createOverlordConnection(ohost string) (*websocket.Conn, error) {
c, _, err := websocket.DefaultDialer.Dial(ohost, nil)
func createOverlordConnection(ourl *url.URL) (*websocket.Conn, error) {
var d websocket.Dialer
if ourl.Scheme == "wss" {
d = websocket.Dialer{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
} else {
d = websocket.Dialer{}
}
ws, _, err := d.Dial(ourl.String(), nil)
if err != nil {
return nil, err
}
return c, nil
return ws, nil
}
func (h *Handler) GetOverlordClient() *OverlordClient {

View file

@ -2,13 +2,18 @@ package worker
import (
"context"
"crypto/tls"
"fmt"
"log"
"net"
"net/http"
"strconv"
"time"
"github.com/giongto35/cloud-game/pkg/config"
"github.com/giongto35/cloud-game/pkg/config/worker"
"golang.org/x/crypto/acme"
"golang.org/x/crypto/acme/autocert"
"github.com/giongto35/cloud-game/pkg/monitoring"
"github.com/golang/glog"
@ -21,6 +26,8 @@ type OverWorker struct {
monitoringServer *monitoring.ServerMonitoring
}
const stagingLEURL = "https://acme-staging-v02.api.letsencrypt.org/directory"
func New(ctx context.Context, cfg worker.Config) *OverWorker {
return &OverWorker{
ctx: ctx,
@ -50,6 +57,87 @@ func (o *OverWorker) Shutdown() {
}
}
func makeServerFromMux(mux *http.ServeMux) *http.Server {
// set timeouts so that a slow or malicious client doesn't
// hold resources forever
return &http.Server{
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
IdleTimeout: 120 * time.Second,
Handler: mux,
}
}
func makeHTTPServer() *http.Server {
mux := &http.ServeMux{}
mux.HandleFunc("/echo", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
log.Println(w, "echo")
})
return makeServerFromMux(mux)
}
func makeHTTPToHTTPSRedirectServer() *http.Server {
handleRedirect := func(w http.ResponseWriter, r *http.Request) {
newURI := "https://" + r.Host + r.URL.String()
http.Redirect(w, r, newURI, http.StatusFound)
}
mux := &http.ServeMux{}
mux.HandleFunc("/", handleRedirect)
return makeServerFromMux(mux)
}
func (o *OverWorker) spawnServer(port int) {
var certManager *autocert.Manager
var httpsSrv *http.Server
if *config.Mode == config.ProdEnv || *config.Mode == config.StagingEnv {
var leurl string
if *config.Mode == config.StagingEnv {
leurl = stagingLEURL
} else {
leurl = acme.LetsEncryptURL
}
certManager = &autocert.Manager{
Prompt: autocert.AcceptTOS,
Cache: autocert.DirCache("assets/cache"),
Client: &acme.Client{DirectoryURL: leurl},
}
httpsSrv = makeHTTPServer()
httpsSrv.Addr = ":443"
httpsSrv.TLSConfig = &tls.Config{GetCertificate: certManager.GetCertificate}
go func() {
fmt.Printf("Starting HTTPS server on %s\n", httpsSrv.Addr)
err := httpsSrv.ListenAndServeTLS("", "")
if err != nil {
log.Printf("httpsSrv.ListendAndServeTLS() failed with %s", err)
}
}()
}
var httpSrv *http.Server
if *config.Mode == config.ProdEnv || *config.Mode == config.StagingEnv {
httpSrv = makeHTTPToHTTPSRedirectServer()
} else {
httpSrv = makeHTTPServer()
}
if certManager != nil {
httpSrv.Handler = certManager.HTTPHandler(httpSrv.Handler)
}
httpSrv.Addr = ":" + strconv.Itoa(port)
err := httpSrv.ListenAndServe()
if err != nil {
log.Printf("httpSrv.ListenAndServe() failed with %s", err)
}
}
// initializeWorker setup a worker
func (o *OverWorker) initializeWorker() {
worker := NewHandler(o.cfg)
@ -77,12 +165,6 @@ func (o *OverWorker) initializeWorker() {
l.Close()
// echo endpoint is where user will request to test latency
http.HandleFunc("/echo", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
fmt.Fprintf(w, "echo")
})
http.ListenAndServe(":"+strconv.Itoa(port), nil)
o.spawnServer(port)
}
}

2
web/game.html vendored
View file

@ -99,7 +99,7 @@
<script src="/static/js/input/input.js?v=1"></script>
<script src="/static/js/gameList.js?v=1"></script>
<script src="/static/js/room.js?v=1"></script>
<script src="/static/js/controller.js?v=1"></script>
<script src="/static/js/controller.js?v=2"></script>
<script src="/static/js/input/keyboard.js?v=1"></script>
<script src="/static/js/input/touch.js?v=1"></script>
<script src="/static/js/input/joystick.js?v=1"></script>

View file

@ -50,7 +50,7 @@
Promise.all((data.addresses || []).map(address => {
let beforeTime = Date.now();
return ajax.fetch(`http://${address}:9000/echo?_=${beforeTime}`, {}, timeoutMs)
return ajax.fetch(`${address}?_=${beforeTime}`, {method: "GET", redirect: "follow"}, timeoutMs)
.then(() => ({[address]: Date.now() - beforeTime}), () => ({[address]: maxTimeoutMs}));
})).then(results => {
// const latencies = Object.assign({}, ...results);

View file

@ -14,7 +14,13 @@ const socket = (() => {
const init = (roomId, zone) => {
const paramString = new URLSearchParams({room_id: roomId, zone: zone})
conn = new WebSocket(`ws://${location.host}/ws?${paramString.toString()}`);
// if localhost
if (location.hostname === "localhost" || location.hostname === "127.0.0.1") {
scheme = "ws"
} else {
scheme = "wss"
}
conn = new WebSocket(`${scheme}://${location.host}/ws?${paramString.toString()}`);
// Clear old roomID
conn.onopen = () => {