diff --git a/go.mod b/go.mod
index 6a79beda..07ab8c75 100644
--- a/go.mod
+++ b/go.mod
@@ -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
)
diff --git a/go.sum b/go.sum
index b3a35bc2..84424ee6 100644
--- a/go.sum
+++ b/go.sum
@@ -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=
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 042a8757..0a606515 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -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
diff --git a/pkg/overlord/config.go b/pkg/overlord/config.go
index eb03e353..4ace79f6 100644
--- a/pkg/overlord/config.go
+++ b/pkg/overlord/config.go
@@ -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
}
diff --git a/pkg/overlord/handlers.go b/pkg/overlord/handlers.go
index 6fbdb0ea..d9f7d891 100644
--- a/pkg/overlord/handlers.go
+++ b/pkg/overlord/handlers.go
@@ -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
}
}
diff --git a/pkg/overlord/overlord.go b/pkg/overlord/overlord.go
index 7b2b704e..d297cdc3 100644
--- a/pkg/overlord/overlord.go
+++ b/pkg/overlord/overlord.go
@@ -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)
}
}
diff --git a/pkg/overlord/worker.go b/pkg/overlord/worker.go
index 03a28b85..1dd2a3ce 100644
--- a/pkg/overlord/worker.go
+++ b/pkg/overlord/worker.go
@@ -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,
diff --git a/pkg/worker/handlers.go b/pkg/worker/handlers.go
index 982ddf12..5ff3af77 100644
--- a/pkg/worker/handlers.go
+++ b/pkg/worker/handlers.go
@@ -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 {
diff --git a/pkg/worker/overworker.go b/pkg/worker/overworker.go
index 62468b35..0474cb45 100644
--- a/pkg/worker/overworker.go
+++ b/pkg/worker/overworker.go
@@ -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)
}
}
diff --git a/web/game.html b/web/game.html
index baa19dbb..89b2c0d5 100644
--- a/web/game.html
+++ b/web/game.html
@@ -99,7 +99,7 @@
-
+
diff --git a/web/js/controller.js b/web/js/controller.js
index ddcd174b..08067a3d 100644
--- a/web/js/controller.js
+++ b/web/js/controller.js
@@ -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);
diff --git a/web/js/network/socket.js b/web/js/network/socket.js
index 060f05ea..f912d017 100644
--- a/web/js/network/socket.js
+++ b/web/js/network/socket.js
@@ -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 = () => {