mirror of https://github.com/gin-gonic/gin.git
206 lines
9.6 KiB
HTML
206 lines
9.6 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Server-Sent Events. Room "{{.roomid}}"</title>
|
|
<!-- jQuery -->
|
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
|
|
<script src="http://malsup.github.com/jquery.form.js"></script>
|
|
<!-- EPOCH -->
|
|
<script src="http://d3js.org/d3.v3.min.js"></script>
|
|
<script src="/static/epoch.min.js"></script>
|
|
<link rel="stylesheet" href="/static/epoch.min.css">
|
|
<script src="/static/realtime.js"></script>
|
|
<!-- Latest compiled and minified CSS -->
|
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
|
|
<!-- Optional theme -->
|
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap-theme.min.css">
|
|
<!-- Latest compiled and minified JavaScript -->
|
|
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
|
|
<!-- Primjs -->
|
|
<link href="/static/prismjs.min.css" rel="stylesheet" />
|
|
|
|
<script type="text/javascript">
|
|
$(document).ready(function() {
|
|
StartRealtime({{.roomid}}, {{.timestamp}});
|
|
});
|
|
</script>
|
|
<style>
|
|
body { padding-top: 50px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<nav class="navbar navbar-fixed-top navbar-inverse">
|
|
<div class="container">
|
|
<div class="navbar-header">
|
|
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
|
<span class="sr-only">Toggle navigation</span>
|
|
<span class="icon-bar"></span>
|
|
<span class="icon-bar"></span>
|
|
<span class="icon-bar"></span>
|
|
</button>
|
|
<a class="navbar-brand" href="#">Server-Sent Events</a>
|
|
</div>
|
|
<div id="navbar" class="collapse navbar-collapse">
|
|
<ul class="nav navbar-nav">
|
|
<li class="active"><a href="#">Demo</a></li>
|
|
<li><a href="http://www.w3.org/TR/2009/WD-eventsource-20091029/">W3 Standard</a></li>
|
|
<li><a href="http://caniuse.com/#feat=eventsource">Browser Support</a></li>
|
|
<li><a href="http://gin-gonic.github.io/gin/">Gin Framework</a></li>
|
|
<li><a href="https://github.com/gin-gonic/gin/tree/develop/examples/realtime-advanced">Github</a></li>
|
|
</ul>
|
|
</div><!-- /.nav-collapse -->
|
|
</div><!-- /.container -->
|
|
</nav><!-- /.navbar -->
|
|
<!-- Main jumbotron for a primary marketing message or call to action -->
|
|
<div class="jumbotron">
|
|
<div class="container">
|
|
<h1>Server-Sent Events in Go</h1>
|
|
<p>Server-sent events (SSE) is a technology where a browser receives automatic updates from a server via HTTP connection. It is not websockets. <a href="http://www.html5rocks.com/en/tutorials/eventsource/basics/">Learn more.</a></p>
|
|
<p>The chat and the charts data is provided in realtime using the SSE implemention of <a href="https://github.com/gin-gonic/gin/blob/15b0c49da556d58a3d934b86e3aa552ff224026d/examples/realtime-chat/main.go#L23-L32">Gin Framework</a>.</p>
|
|
<div class="row">
|
|
<div class="col-md-8">
|
|
<div id="chat-scroll" style="overflow-y:scroll; overflow-x:scroll; height:270px">
|
|
<table id="table-style" class="table" data-show-header="false">
|
|
<thead>
|
|
<tr>
|
|
<th data-field="nick" class="col-md-2">Nick</th>
|
|
<th data-field="message" class="col-md-8">Message</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="chat"></tbody>
|
|
</table>
|
|
</div>
|
|
{{if .nick}}
|
|
<form autocomplete="off" class="form-inline" id="chat-form" action="/room-post/{{.roomid}}?nick={{.nick}}" method="post">
|
|
<div class="form-group">
|
|
<label class="sr-only" for="chat-message">Message</label>
|
|
<div class="input-group">
|
|
<div class="input-group-addon">{{.nick}}</div>
|
|
<input type="text" name="message" id="chat-message" class="form-control" placeholder="a message" value="" />
|
|
</div>
|
|
</div>
|
|
<input type="submit" class="btn btn-primary" value="Send" />
|
|
</form>
|
|
{{end}}
|
|
</div>
|
|
<div class="col-md-4">
|
|
{{if .nick}}
|
|
<h3>Inbound/Outbound</h3>
|
|
<div id="messagesChart" class="epoch category20c"></div>
|
|
{{else}}
|
|
<form action="" method="get">
|
|
<legend>Join the SSE real-time chat</legend>
|
|
<div class="form-group">
|
|
<label for="nick">Your Name</label>
|
|
<input value='' name="nick" id="nick" placeholder="John" type="text" class="form-control" />
|
|
</div>
|
|
<div class="form-group text-center">
|
|
<input type="submit" class="btn btn-success btn-login-submit" value="Join" />
|
|
</div>
|
|
</form>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="container">
|
|
<div class="row">
|
|
<h2>Realtime server Go stats</h2>
|
|
<div class="col-md-4">
|
|
<h3>Number of Goroutines</h3>
|
|
<p>
|
|
<div id="goroutinesChart" class="epoch category20c"></div>
|
|
</p>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<h3>HEAP/Stack bytes</h3>
|
|
<p>
|
|
<div id="heapChart" class="epoch category20b"></div>
|
|
</p>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<h3>Mallocs/Frees</h3>
|
|
<p>
|
|
<div id="mallocsChart" class="epoch category10"></div>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<h2>MIT Open Sourced</h2>
|
|
<ul>
|
|
<li><a href="https://github.com/gin-gonic/gin/tree/develop/examples/realtime-advanced">This demo website (JS and Go)</a></li>
|
|
<li><a href="https://github.com/manucorporat/sse">The SSE implementation in Go</a></li>
|
|
<li><a href="https://github.com/gin-gonic/gin">The Web Framework (Gin)</a></li>
|
|
</ul>
|
|
<div class="col-md-6">
|
|
<script src="/static/prismjs.min.js"></script>
|
|
<h3>Server-side (Go)</h3>
|
|
<pre><code class="language-go">func streamRoom(c *gin.Context) {
|
|
roomid := c.ParamValue("roomid")
|
|
listener := openListener(roomid)
|
|
statsTicker := time.NewTicker(1 * time.Second)
|
|
defer closeListener(roomid, listener)
|
|
defer statsTicker.Stop()
|
|
|
|
c.Stream(func(w io.Writer) bool {
|
|
select {
|
|
case msg := <-listener:
|
|
c.SSEvent("message", msg)
|
|
case <-statsTicker.C:
|
|
c.SSEvent("stats", Stats())
|
|
}
|
|
return true
|
|
})
|
|
}</code></pre>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h3>Client-side (JS)</h3>
|
|
<pre><code class="language-javascript">function StartSSE(roomid) {
|
|
var source = new EventSource('/stream/'+roomid);
|
|
source.addEventListener('message', newChatMessage, false);
|
|
source.addEventListener('stats', stats, false);
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-12">
|
|
<h3>SSE package</h3>
|
|
<pre><code class="language-go">import "github.com/manucorporat/sse"
|
|
|
|
func httpHandler(w http.ResponseWriter, req *http.Request) {
|
|
// data can be a primitive like a string, an integer or a float
|
|
sse.Encode(w, sse.Event{
|
|
Event: "message",
|
|
Data: "some data\nmore data",
|
|
})
|
|
|
|
// also a complex type, like a map, a struct or a slice
|
|
sse.Encode(w, sse.Event{
|
|
Id: "124",
|
|
Event: "message",
|
|
Data: map[string]interface{}{
|
|
"user": "manu",
|
|
"date": time.Now().Unix(),
|
|
"content": "hi!",
|
|
},
|
|
})
|
|
}</code></pre>
|
|
<pre>event: message
|
|
data: some data\\nmore data
|
|
|
|
id: 124
|
|
event: message
|
|
data: {"content":"hi!","date":1431540810,"user":"manu"}</pre>
|
|
</div>
|
|
</div>
|
|
<hr>
|
|
<footer>
|
|
<p>Created with <span class="glyphicon glyphicon-heart"></span> by <a href="https://github.com/manucorporat">Manu Martinez-Almeida</a></p>
|
|
</footer>
|
|
</div>
|
|
</body>
|
|
</html>
|