func runwebsite() { ilimit, _ := strconv.ParseInt(limitpersecond, 10, 64) fmt.Println("response time is " + responsetime) fmt.Println("limitpersecond is " + limitpersecond) limiter := tollbooth.NewLimiter(ilimit, time.Second) http.Handle("/", tollbooth.LimitFuncHandler(limiter, slowHandler)) log.Fatal(http.ListenAndServe(":8888", nil)) }
// Serve all files in the current directory, or only a few select filetypes (html, css, js, png and txt) func registerHandlers(mux *http.ServeMux, handlePath, servedir string, perm pinterface.IPermissions, luapool *lStatePool, cache *FileCache, addDomain bool) { // Handle all requests with this function allRequests := func(w http.ResponseWriter, req *http.Request) { if perm.Rejected(w, req) { // Get and call the Permission Denied function perm.DenyFunction()(w, req) // Reject the request by returning return } // Local to this function servedir := servedir // Look for the directory that is named the same as the host if addDomain { servedir = filepath.Join(servedir, getDomain(req)) } urlpath := req.URL.Path filename := url2filename(servedir, urlpath) // Remove the trailing slash from the filename, if any noslash := filename if strings.HasSuffix(filename, pathsep) { noslash = filename[:len(filename)-1] } hasdir := fs.exists(filename) && fs.isDir(filename) dirname := filename hasfile := fs.exists(noslash) // Set the server header. serverHeaders(w) // Share the directory or file if hasdir { dirPage(w, req, servedir, dirname, perm, luapool, cache) return } else if !hasdir && hasfile { // Share a single file instead of a directory filePage(w, req, noslash, perm, luapool, cache) return } // Not found w.WriteHeader(http.StatusNotFound) fmt.Fprint(w, noPage(filename)) } // Handle requests differently depending on if rate limiting is enabled or not if disableRateLimiting { mux.HandleFunc(handlePath, allRequests) } else { limiter := tollbooth.NewLimiter(limitRequests, time.Second) limiter.MessageContentType = "text/html; charset=utf-8" limiter.Message = easyPage("Rate-limit exceeded", "<div style='color:red'>You have reached the maximum request limit.</div>") mux.Handle(handlePath, tollbooth.LimitFuncHandler(limiter, allRequests)) } }
// Make functions related to handling HTTP requests available to Lua scripts func exportLuaHandlerFunctions(L *lua.LState, filename string, perm pinterface.IPermissions, luapool *lStatePool, cache *FileCache, mux *http.ServeMux, addDomain bool) { L.SetGlobal("handle", L.NewFunction(func(L *lua.LState) int { handlePath := L.ToString(1) handleFunc := L.ToFunction(2) wrappedHandleFunc := func(w http.ResponseWriter, req *http.Request) { // Set up a new Lua state with the current http.ResponseWriter and *http.Request exportCommonFunctions(w, req, filename, perm, L, luapool, nil, cache) // Then run the given Lua function L.Push(handleFunc) if err := L.PCall(0, lua.MultRet, nil); err != nil { // Non-fatal error log.Error("Handler for "+handlePath+" failed:", err) } } // Handle requests differently depending on if rate limiting is enabled or not if disableRateLimiting { mux.HandleFunc(handlePath, wrappedHandleFunc) } else { limiter := tollbooth.NewLimiter(limitRequests, time.Second) limiter.MessageContentType = "text/html; charset=utf-8" limiter.Message = easyPage("Rate-limit exceeded", "<div style='color:red'>You have reached the maximum request limit.</div>") mux.Handle(handlePath, tollbooth.LimitFuncHandler(limiter, wrappedHandleFunc)) } return 0 // number of results })) L.SetGlobal("servedir", L.NewFunction(func(L *lua.LState) int { handlePath := L.ToString(1) // serve as (ie. "/") rootdir := L.ToString(2) // filesystem directory (ie. "./public") rootdir = filepath.Join(filepath.Dir(filename), rootdir) registerHandlers(mux, handlePath, rootdir, perm, luapool, cache, addDomain) return 0 // number of results })) }
func main() { // Configure logging: http://godoc.org/github.com/op/go-logging#Formatter loggingBackend := logging.NewLogBackend(os.Stdout, "", 0) logFormat := logging.MustStringFormatter( // https://golang.org/pkg/time/#Time.Format `%{time:Mon Jan 2 15:04:05 MST 2006} - %{level:.4s} - %{shortfile} - %{message}`, ) loggingBackendFormatted := logging.NewBackendFormatter(loggingBackend, logFormat) logging.SetBackend(loggingBackendFormatted) // Load the .env file which contains environment variables with secret values err := godotenv.Load(projectPath + "/.env") if err != nil { log.Fatal("Failed to load .env file:", err) } // Create a session store sessionSecret := os.Getenv("SESSION_SECRET") sessionStore = sessions.NewCookieStore([]byte(sessionSecret)) maxAge := 60 * 60 * 24 * 30 // 1 month if useSSL == true { sessionStore.Options = &sessions.Options{ Domain: domain, Path: "/", MaxAge: maxAge, Secure: true, // Only send the cookie over HTTPS: https://www.owasp.org/index.php/Testing_for_cookies_attributes_(OTG-SESS-002) HttpOnly: true, // Mitigate XSS attacks: https://www.owasp.org/index.php/HttpOnly } } else { sessionStore.Options = &sessions.Options{ Domain: domain, Path: "/", MaxAge: maxAge, HttpOnly: true, // Mitigate XSS attacks: https://www.owasp.org/index.php/HttpOnly } } // Initialize the database model if db, err = models.GetModels(projectPath + "/database.sqlite"); err != nil { log.Fatal("Failed to open the database:", err) } // Clean up any non-started games before we start if leftoverGames, err := db.Games.Cleanup(); err != nil { log.Fatal("Failed to cleanup the leftover games:", err) } else { for _, gameID := range leftoverGames { log.Info("Deleted game", gameID, "during starting cleanup.") } } // Initialize the achievements //achievementsInit() // Create a WebSocket router using the Golem framework router := golem.NewRouter() router.SetConnectionExtension(NewExtendedConnection) router.OnHandshake(validateSession) router.OnConnect(connOpen) router.OnClose(connClose) /* * The websocket commands */ // Chat commands router.On("roomJoin", roomJoin) router.On("roomLeave", roomLeave) router.On("roomMessage", roomMessage) router.On("privateMessage", privateMessage) // Game commands router.On("gameCreate", gameCreate) router.On("gameJoin", gameJoin) router.On("gameLeave", gameLeave) router.On("gameStart", gameStart) // Action commands router.On("actionPlay", actionPlay) router.On("actionClue", actionClue) router.On("actionDiscard", actionDiscard) // Profile commands /*router.On("profileGet", profileGet) router.On("profileSetUsername", profileSetUsername)*/ // Admin commands /*router.On("adminBan", adminBan) router.On("adminUnban", adminUnban) router.On("adminBanIP", adminBanIP) router.On("adminUnbanIP", adminUnbanIP) router.On("adminSquelch", adminSquelch) router.On("adminUnsquelch", adminUnsquelch) router.On("adminPromote", adminPromote) router.On("adminDemote", adminDemote)*/ /* * HTTP stuff */ // Minify CSS and JS m := minify.New() m.AddFunc("text/css", css.Minify) for _, fileName := range []string{"main"} { inputFile, _ := os.Open("public/css/" + fileName + ".css") outputFile, _ := os.Create("public/css/" + fileName + ".min.css") if err := m.Minify("text/css", outputFile, inputFile); err != nil { log.Error("Failed to minify \""+fileName+".css\":", err) } } m.AddFunc("text/javascript", js.Minify) for _, fileName := range []string{"login", "main"} { inputFile, _ := os.Open("public/js/" + fileName + ".js") outputFile, _ := os.Create("public/js/" + fileName + ".min.js") if err := m.Minify("text/javascript", outputFile, inputFile); err != nil { log.Error("Failed to minify \""+fileName+".js\":", err) } } // Assign functions to URIs http.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir("public")))) // Serve static files http.HandleFunc("/", httpHandler) // Anything that is not a static file will match this http.Handle("/login", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, time.Second), loginHandler)) // Rate limit the login handler http.HandleFunc("/logout", logoutHandler) http.HandleFunc("/ws", router.Handler()) // The golem router handles websockets /* * Start the server */ // Figure out the port that we are using for the HTTP server var port int if useSSL == true { port = 443 } else { port = 80 } // Welcome message log.Info("Starting hanabi-server on port " + strconv.Itoa(port) + ".") // Listen and serve if useSSL == true { if err := http.ListenAndServeTLS( ":"+strconv.Itoa(port), // Nothing before the colon implies 0.0.0.0 sslCertFile, sslKeyFile, context.ClearHandler(http.DefaultServeMux), // We wrap with context.ClearHandler or else we will leak memory: http://www.gorillatoolkit.org/pkg/sessions ); err != nil { log.Fatal("ListenAndServeTLS failed:", err) } } else { // Listen and serve (HTTP) if err := http.ListenAndServe( ":"+strconv.Itoa(port), // Nothing before the colon implies 0.0.0.0 context.ClearHandler(http.DefaultServeMux), // We wrap with context.ClearHandler or else we will leak memory: http://www.gorillatoolkit.org/pkg/sessions ); err != nil { log.Fatal("ListenAndServe failed:", err) } } }
func main() { if parseFlags() { return } if err := loadCountries(); err != nil { log.Fatalf("error loading countries.json: %s", err.Error()) } inst := instance{} { var err error ident, err := dht.NewIdent() if err != nil { log.Fatalf("error creating new dht identity: %s", err) } inst.Ident = ident udpTransport, err := transport.NewUDPTransport("udp", ":33450") if err != nil { log.Fatalf("error creating new udp transport instance: %s", err) } udpTransport.Handle(dht.PacketIDSendNodes, inst.handleSendNodesPacket) udpTransport.Handle(bootstrap.PacketIDBootstrapInfo, handleBootstrapInfoPacket) inst.UDPTransport = udpTransport /*tcpTransport, err = transport.NewTCPTransport("tcp", ":33450") if err != nil { panic(err) } inst.TCPTransport = tcpTransport*/ } //handle stop signal interruptChan := make(chan os.Signal) signal.Notify(interruptChan, os.Interrupt) //setup http server listener, err := net.Listen("tcp", fmt.Sprintf(":%d", httpListenPort)) if err != nil { log.Fatalf("error in net.Listen: %s", err.Error()) } limiter := tollbooth.NewLimiter(1, 2*time.Second) limiter.Methods = []string{"POST"} limiter.IPLookups = []string{"X-Forwarded-For", "RemoteAddr", "X-Real-IP"} serveMux := http.NewServeMux() serveMux.HandleFunc("/", handleHTTPRequest) serveMux.Handle("/test", tollbooth.LimitFuncHandler(limiter, handleHTTPRequest)) serveMux.HandleFunc("/json", handleJSONRequest) go func() { err := http.Serve(listener, serveMux) if err != nil { log.Printf("http server error: %s\n", err.Error()) interruptChan <- os.Interrupt } }() //listen for tox packets go func() { err := inst.UDPTransport.Listen() if err != nil { log.Printf("udp transport error: %s\n", err.Error()) interruptChan <- os.Interrupt } }() //go tcpTransport.Listen() err = refreshNodes() if err != nil { log.Fatal(err.Error()) } inst.probeNodes() probeTicker := time.NewTicker(probeRate) refreshTicker := time.NewTicker(refreshRate) updateTicker := time.NewTicker(30 * time.Second) run := true for run { select { case <-interruptChan: fmt.Printf("killing routines\n") probeTicker.Stop() refreshTicker.Stop() updateTicker.Stop() inst.UDPTransport.Stop() //tcpTransport.Stop() listener.Close() run = false case <-probeTicker.C: // we want an empty ping list at the start of every probe pingsMutex.Lock() pings.Clear(false) pingsMutex.Unlock() nodesMutex.Lock() err := inst.probeNodes() nodesMutex.Unlock() if err != nil { log.Printf("error while trying to probe nodes: %s", err.Error()) } case <-refreshTicker.C: err := refreshNodes() if err != nil { log.Printf("error while trying to refresh nodes: %s", err.Error()) } case <-updateTicker.C: pingsMutex.Lock() pings.Clear(true) pingsMutex.Unlock() nodesMutex.Lock() for _, node := range nodes { if time.Now().Sub(time.Unix(node.LastPing, 0)) > time.Minute*2 { node.UDPStatus = false } } sort.Stable(nodeSlice(nodes)) nodesMutex.Unlock() } } }
func main() { // Configure logging: http://godoc.org/github.com/op/go-logging#Formatter log = &CustomLogger{ Logger: logging.MustGetLogger("isaac"), } loggingBackend := logging.NewLogBackend(os.Stdout, "", 0) logFormat := logging.MustStringFormatter( // https://golang.org/pkg/time/#Time.Format //`%{time:Mon Jan 2 15:04:05 MST 2006} - %{level:.4s} - %{shortfile} - %{message}`, // We no longer use the line number since the log struct extension breaks it `%{time:Mon Jan 2 15:04:05 MST 2006} - %{level:.4s} - %{message}`, ) loggingBackendFormatted := logging.NewBackendFormatter(loggingBackend, logFormat) logging.SetBackend(loggingBackendFormatted) // Load the .env file which contains environment variables with secret values err := godotenv.Load(projectPath + "/.env") if err != nil { log.Fatal("Failed to load .env file:", err) } // Configure error reporting to Sentry sentrySecret := os.Getenv("SENTRY_SECRET") raven.SetDSN("https://*****:*****@sentry.io/124813") // Welcome message log.Info("-----------------------------") log.Info("Starting isaac-racing-server.") log.Info("-----------------------------") // Create a session store sessionSecret := os.Getenv("SESSION_SECRET") sessionStore = sessions.NewCookieStore([]byte(sessionSecret)) maxAge := 5 // 5 seconds if useSSL == true { sessionStore.Options = &sessions.Options{ Domain: domain, Path: "/", MaxAge: maxAge, Secure: true, // Only send the cookie over HTTPS: https://www.owasp.org/index.php/Testing_for_cookies_attributes_(OTG-SESS-002) HttpOnly: true, // Mitigate XSS attacks: https://www.owasp.org/index.php/HttpOnly } } else { sessionStore.Options = &sessions.Options{ Domain: domain, Path: "/", MaxAge: maxAge, HttpOnly: true, // Mitigate XSS attacks: https://www.owasp.org/index.php/HttpOnly } } // Initialize the database model if db, err = models.GetModels(projectPath + "/database.sqlite"); err != nil { log.Fatal("Failed to open the database:", err) } // Clean up any non-started races before we start if nonStartedRaces, err := db.Races.Cleanup(); err != nil { log.Fatal("Failed to cleanup the leftover races:", err) } else { for _, raceID := range nonStartedRaces { log.Info("Deleted race", raceID, "during starting cleanup.") } } // Initiate the "end of race" function for each of the non-finished races in 30 minutes // (this is normally initiated on race start) if startedRaces, err := db.Races.GetCurrentRaces(); err != nil { log.Fatal("Failed to start the leftover races:", err) } else { for _, race := range startedRaces { go raceCheckStart3(race.ID) } } // Initialize the achievements achievementsInit() // Start the Twitch bot go twitchInit() // Create a WebSocket router using the Golem framework router := golem.NewRouter() router.SetConnectionExtension(NewExtendedConnection) router.OnHandshake(validateSession) router.OnConnect(connOpen) router.OnClose(connClose) /* The websocket commands */ // Chat commands router.On("roomJoin", roomJoin) router.On("roomLeave", roomLeave) router.On("roomMessage", roomMessage) router.On("privateMessage", privateMessage) router.On("roomListAll", roomListAll) // Race commands router.On("raceCreate", raceCreate) router.On("raceJoin", raceJoin) router.On("raceLeave", raceLeave) router.On("raceReady", raceReady) router.On("raceUnready", raceUnready) router.On("raceRuleset", raceRuleset) router.On("raceFinish", raceFinish) router.On("raceQuit", raceQuit) router.On("raceComment", raceComment) router.On("raceItem", raceItem) router.On("raceFloor", raceFloor) // Profile commands router.On("profileGet", profileGet) router.On("profileSetUsername", profileSetUsername) router.On("profileSetStreamURL", profileSetStreamURL) router.On("profileSetTwitchBotEnabled", profileSetTwitchBotEnabled) router.On("profileSetTwitchBotDelay", profileSetTwitchBotDelay) // Admin commands router.On("adminBan", adminBan) router.On("adminUnban", adminUnban) router.On("adminBanIP", adminBanIP) router.On("adminUnbanIP", adminUnbanIP) router.On("adminMute", adminMute) router.On("adminUnmute", adminUnmute) router.On("adminPromote", adminPromote) router.On("adminDemote", adminDemote) // Miscellaneous router.On("logout", logout) router.On("debug", debug) /* HTTP stuff */ // Minify CSS and JS m := minify.New() m.AddFunc("text/css", css.Minify) for _, fileName := range []string{"main", "ie8"} { inputFile, _ := os.Open("public/css/" + fileName + ".css") outputFile, _ := os.Create("public/css/" + fileName + ".min.css") if err := m.Minify("text/css", outputFile, inputFile); err != nil { log.Error("Failed to minify \""+fileName+".css\":", err) } } m.AddFunc("text/javascript", js.Minify) for _, fileName := range []string{"main", "util"} { inputFile, _ := os.Open("public/js/" + fileName + ".js") outputFile, _ := os.Create("public/js/" + fileName + ".min.js") if err := m.Minify("text/javascript", outputFile, inputFile); err != nil { log.Error("Failed to minify \""+fileName+".js\":", err) } } // Set up the Pat HTTP router p := pat.New() p.Get("/", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, time.Second), httpHome)) p.Get("/news", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, time.Second), httpNews)) p.Get("/races", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, time.Second), httpRaces)) p.Get("/profiles", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, time.Second), httpProfiles)) p.Get("/leaderboards", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, time.Second), httpLeaderboards)) p.Get("/info", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, time.Second), httpInfo)) p.Get("/download", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, time.Second), httpDownload)) p.Post("/login", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, time.Second), loginHandler)) p.Post("/register", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, time.Second), registerHandler)) /* Assign functions to URIs */ // Normal requests get assigned to the Pat HTTP router http.Handle("/", p) // Files in the "public" subdirectory are just images/css/javascript files http.Handle("/public/", http.StripPrefix("/public/", http.FileServer(justFilesFilesystem{http.Dir("public")}))) // Websockets are handled by the Golem websocket router http.HandleFunc("/ws", router.Handler()) /* Start the server */ // Figure out the port that we are using for the HTTP server var port int if useSSL == true { // We want all HTTP requests to be redirected, but we need to make an exception for Let's Encrypt // The previous "Handle" and "HandleFunc" were being added to the default serve mux // We need to create a new fresh one for the HTTP handler HTTPServeMux := http.NewServeMux() HTTPServeMux.Handle("/.well-known/acme-challenge/", http.FileServer(http.FileSystem(http.Dir("letsencrypt")))) HTTPServeMux.Handle("/", http.HandlerFunc(HTTPRedirect)) // ListenAndServe is blocking, so start listening on a new goroutine go http.ListenAndServe(":80", HTTPServeMux) // Nothing before the colon implies 0.0.0.0 // 443 is the default port for HTTPS port = 443 } else { // 80 is the defeault port for HTTP port = 80 } // Listen and serve log.Info("Listening on port " + strconv.Itoa(port) + ".") if useSSL == true { if err := http.ListenAndServeTLS( ":"+strconv.Itoa(port), // Nothing before the colon implies 0.0.0.0 sslCertFile, sslKeyFile, context.ClearHandler(http.DefaultServeMux), // We wrap with context.ClearHandler or else we will leak memory: http://www.gorillatoolkit.org/pkg/sessions ); err != nil { log.Fatal("ListenAndServeTLS failed:", err) } } else { // Listen and serve (HTTP) if err := http.ListenAndServe( ":"+strconv.Itoa(port), // Nothing before the colon implies 0.0.0.0 context.ClearHandler(http.DefaultServeMux), // We wrap with context.ClearHandler or else we will leak memory: http://www.gorillatoolkit.org/pkg/sessions ); err != nil { log.Fatal("ListenAndServeTLS failed:", err) } } }