I have created a Golang WebApp that shows the time. I am new to the Golang programming language so thought this little app would allow me to use golang as a webserver, learn about goroutines and websockets which I am also new to.
I would like to know if my code follows best practices especially regarding goroutines/threading and the use of websockets.
Index.html
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
</head>
<body>
<!--[if lt IE 8]>
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
<![endif]-->
<h1>Golang Clock WebApp</h1>
<h3>
<span id="name">Header Text</span>
<button id="get-time-manually">Update Time</button>
</h3>
<hr/>
<div id="container"></div>
<script type="text/javascript">
$( document ).ready(function() {
getTime();
$( "#get-time-manually" ).click(function() {
getTime();
});
var ws;
if (window.WebSocket === undefined) {
$("#container").append("Your browser does not support WebSockets");
return;
} else {
ws = initWS();
}
});
function getTime() {
$.get( "/time", function( data ) {
$( "#name" ).html( JSON.parse(data) );
});
};
function initWS() {
var socket = new WebSocket("ws://localhost:8080/ws"),
container = $("#container")
socket.onmessage = function (e) {
$( "#name" ).html( JSON.parse(e.data) );
};
return socket;
};
</script>
</body>
</html>
main.go
package main
import (
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
)
func newRouter() *mux.Router {
router := mux.NewRouter()
router.HandleFunc("/time", getTimeHandler).Methods("GET")
router.HandleFunc("/ws", getWebSocketHandler)
// /assets/ Page
staticFileDirectory := http.Dir("./assets/")
staticFileHandler := http.StripPrefix("/assets/", http.FileServer(staticFileDirectory))
router.PathPrefix("/assets/").Handler(staticFileHandler).Methods("GET")
return router
}
func main() {
router := newRouter()
http.ListenAndServe(":8080", router)
}
func getTimeHandler(w http.ResponseWriter, r *http.Request) {
timeBytes, err := json.Marshal(getTimeNow())
// If there is an error, print it to the console, and return a server
// error response to the user
if err != nil {
fmt.Println(fmt.Errorf("Error: %v", err))
w.WriteHeader(http.StatusInternalServerError)
return
}
// If all goes well, write the JSON to the response
w.Write(timeBytes)
}
func getWebSocketHandler(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Origin") != "http://"+r.Host {
//"ws://" + location.host + "/ws"
http.Error(w, "Origin not allowed", 403)
return
}
conn, err := websocket.Upgrade(w, r, w.Header(), 1024, 1024)
if err != nil {
http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
}
go timeRoutine(conn)
}
func timeRoutine(conn *websocket.Conn) {
t := time.NewTicker(time.Second * 3)
for {
timeString := getTimeNow()
if err := conn.WriteJSON(timeString); err != nil {
fmt.Println(err)
}
<-t.C
}
}
func getTimeNow() string {
timeNow := time.Now()
formattedTime := timeNow.Format("Mon Jan 02 15:04:05 MST 2006")
return formattedTime
}
The full repo can be found here