Skip to content

Commit

Permalink
Adds a chat sample for socketio
Browse files Browse the repository at this point in the history
  • Loading branch information
casualjim committed Jan 16, 2011
1 parent f69f843 commit 40c66fd
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 23 deletions.
29 changes: 18 additions & 11 deletions core/src/main/scala/org/scalatra/CSRFTokenSupport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,23 @@ package org.scalatra

import java.security.SecureRandom

object GenerateId {
def apply(): String = {
generateCSRFToken
}

private def hexEncode(bytes: Array[Byte]) = ((new StringBuilder(bytes.length * 2) /: bytes) { (sb, b) =>
if((b.toInt & 0xff) < 0x10) sb.append("0")
sb.append(Integer.toString(b.toInt & 0xff, 16))
}).toString

protected def generateCSRFToken = {
val tokenVal = new Array[Byte](20)
(new SecureRandom).nextBytes(tokenVal)
hexEncode(tokenVal)
}
}

trait CSRFTokenSupport { self: ScalatraKernel =>

protected def csrfKey = ScalatraKernel.csrfKey
Expand All @@ -15,17 +32,7 @@ trait CSRFTokenSupport { self: ScalatraKernel =>
}

protected def prepareCSRFToken = {
session.getOrElseUpdate(csrfKey, generateCSRFToken)
session.getOrElseUpdate(csrfKey, GenerateId())
}

private def hexEncode(bytes: Array[Byte]) = ((new StringBuilder(bytes.length * 2) /: bytes) { (sb, b) =>
if((b.toInt & 0xff) < 0x10) sb.append("0")
sb.append(Integer.toString(b.toInt & 0xff, 16))
}).toString

protected def generateCSRFToken = {
val tokenVal = new Array[Byte](20)
(new SecureRandom).nextBytes(tokenVal)
hexEncode(tokenVal)
}
}
43 changes: 43 additions & 0 deletions example/src/main/scala/org/scalatra/ChatServlet.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.scalatra

import socketio.SocketIOSupport
import com.glines.socketio.server.SocketIOFrame

class ChatServlet extends ScalatraServlet with SocketIOSupport {


socketio { socket =>
socket.onConnect { connection =>
println("Connecting chat client [%s]" format connection.clientId)
try {
connection.send(SocketIOFrame.JSON_MESSAGE_TYPE, """{ "welcome": "Welcome to Socket IO chat" }""")
} catch {
case _ => connection.disconnect
}
connection.broadcast(SocketIOFrame.JSON_MESSAGE_TYPE,
"""{ "announcement": "New participant [%s]" }""".format(connection.clientId))
}

socket.onDisconnect { (connection, reason, _) =>
println("Disconnecting chat client [%s] (%s)".format(connection.clientId, reason))
connection.broadcast(SocketIOFrame.JSON_MESSAGE_TYPE,
"""{ "announcement": "Participant [%s] left" }""".format(connection.clientId))
}

socket.onMessage { (connection, _, message) =>
println("RECV: [%s]" format message)
message match {
case "/rclose" => {
connection.close
}
case "/rdisconnect" => {
connection.disconnect
}
case _ => {
connection.broadcast(SocketIOFrame.JSON_MESSAGE_TYPE,
"""{ "message": ["%s", "%s"] }""".format(connection.clientId, message))
}
}
}
}
}
3 changes: 0 additions & 3 deletions example/src/main/scala/org/scalatra/SocketIOExample.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package org.scalatra

import java.lang.String
import javax.servlet.http.HttpServletRequest
import collection.immutable.HashSet
import socketio.SocketIOSupport


Expand Down
5 changes: 5 additions & 0 deletions example/src/main/scala/org/scalatra/TemplateExample.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class TemplateExample extends ScalatraServlet with UrlSupport /*with FileUploadS
<a href={url("/login")}>login</a>
<a href={url("/logout")}>logout</a>
<a href={url("/filter-example")}>filter demo</a>
<a href={url("/chat")}>chat demo</a>
</body>
</html>
}
Expand Down Expand Up @@ -103,6 +104,10 @@ class TemplateExample extends ScalatraServlet with UrlSupport /*with FileUploadS
)
}

get("/chat") {
renderTemplate("chat.ssp")
}

post("/login") {
(params("first"), params("last")) match {
case (first:String, last:String) => {
Expand Down
26 changes: 25 additions & 1 deletion example/src/main/webapp/WEB-INF/web.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,31 @@
<param-value>8080</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>ChatApplication</servlet-name>
<servlet-class>org.scalatra.ChatServlet</servlet-class>
<init-param>
<param-name>flashPolicyServerHost</param-name>
<param-value>localhost</param-value>
</init-param>
<init-param>
<param-name>flashPolicyServerPort</param-name>
<param-value>843</param-value>
</init-param>
<init-param>
<param-name>flashPolicyDomain</param-name>
<param-value>localhost</param-value>
</init-param>
<init-param>
<param-name>flashPolicyPorts</param-name>
<param-value>8080</param-value>
</init-param>
</servlet>

<servlet-mapping>
<servlet-name>ChatApplication</servlet-name>
<url-pattern>/socket.io/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>TemplateExample</servlet-name>
<url-pattern>/*</url-pattern>
Expand All @@ -43,7 +67,7 @@
</servlet-mapping>
<servlet-mapping>
<servlet-name>SocketIOExample</servlet-name>
<url-pattern>/socket.io/*</url-pattern>
<url-pattern>/echoserver/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
Expand Down
105 changes: 105 additions & 0 deletions example/src/main/webapp/chat.ssp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<!doctype html>
<html>
<head>
<title>socket.io client test</title>

<script src="/js/json.js"></script> <!-- for ie -->
<script src="/socket.io/socket.io.js"></script>
</head>
<body>

<script>
function addLine(txt) {
var el = document.createElement('p');
el.innerHTML = txt;
document.getElementById('chat').appendChild(el);
document.getElementById('chat').scrollTop = 1000000;
}
function message(obj){
if ('welcome' in obj) addLine('<em>' + esc(obj.welcome) + '</em>');
else if ('announcement' in obj) addLine('<em>' + esc(obj.announcement) + '</em>');
else if ('message' in obj) addLine('<b>' + esc(obj.message[0]) + ':</b> ' + esc(obj.message[1]));
}

function send(){
var val = document.getElementById('text').value;
if (val == '/lclose') {
addLine('<em>closing...</em>');
socket.close();
} else if (val == '/ldisconnect') {
addLine('<em>disconnecting...</em>');
socket.disconnect();
} else {
socket.send(val);
}
message({ message: ['you', val] });
document.getElementById('text').value = '';
}

function esc(msg){
return msg.replace(/</g, '&lt;').replace(/>/g, '&gt;');
};

function drname(dr) {
if (dr == 1) {
return 'CONNECT_FAILED';
} else if (dr == 2) {
return 'DISCONNECT';
} else if (dr == 3) {
return 'TIMEOUT';
} else if (dr == 4) {
return 'CLOSE_FAILED';
} else if (dr == 5) {
return 'ERROR';
} else if (dr == 6) {
return 'CLOSED_REMOTELY';
} else if (dr == 7) {
return 'CLOSED';
} else {
return 'UNKNOWN['+dr+']';
}
}

var socket = new io.Socket(null, {});
socket.setMessageParser(socket.JSON_MESSAGE, {
encode: function(obj) {
return JSON.stringify(obj);
},
decode: function(str) {
return JSON.parse(str);
}
});
socket.connect();
socket.on('message', function(mtype, obj){
// if (mtype == 1) {
message(obj);
// }
});
socket.on('connect', function(reason, error){
document.getElementById('form').style.display='block';
document.getElementById('chat').innerHTML = '';
});
socket.on('disconnect', function(reason, error){
addLine('<b>Disconnected['+drname(reason)+']: '+error+'</b>');
});
</script>

<h1>Sample chat client</h1>
<div id="chat"><p>Connecting...</p></div>
<form id="form" onsubmit="send(); return false">
<input type="text" autocomplete="off" id="text"><input type="submit" value="Send">
</form>

<style>
#chat { height: 300px; overflow: auto; width: 800px; border: 1px solid #eee; font: 13px Helvetica, Arial; }
#chat p { padding: 8px; margin: 0; }
#chat p:nth-child(odd) { background: #F6F6F6; }
#form { width: 782px; background: #333; padding: 5px 10px; display: none; }
#form input[type=text] { width: 700px; padding: 5px; background: #fff; border: 1px solid #fff; }
#form input[type=submit] { cursor: pointer; background: #999; border: none; padding: 6px 8px; -moz-border-radius: 8px; -webkit-border-radius: 8px; margin-left: 5px; text-shadow: 0 1px 0 #fff; }
#form input[type=submit]:hover { background: #A2A2A2; }
#form input[type=submit]:active { position: relative; top: 2px; }
</style>

</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ object SocketIOSupport {
_messageHandler = Option(callback)
}

def result = {
def result(removeFromClients: SocketIOClient => Unit) = {
new SocketIOClient {
def onConnect(out: SocketIOOutbound) = {
_out = Option(out)
Expand All @@ -54,6 +54,7 @@ object SocketIOSupport {
_disconnectHandler foreach {
_(this, reason, message)
}
removeFromClients(this)
}

def onMessage(messageType: Int, message: String) = {
Expand All @@ -67,9 +68,14 @@ object SocketIOSupport {
}

trait SocketIOClient extends SocketIOInbound {

val clientId = GenerateId()
protected var _out: Option[SocketIOOutbound] = None
protected[socketio] var clients = new CopyOnWriteArrayList[SocketIOClient]
private var _broadcaster: (Int, String) => Unit = null

def broadcaster(block: (Int, String) => Unit) {
_broadcaster = block
}
def onMessage(messageType: Int, message: String)

def onDisconnect(reason: DisconnectReason, message: String)
Expand All @@ -78,9 +84,9 @@ object SocketIOSupport {

def getProtocol = null

def send(messageType: FrameType, message: String) {
def send(messageType: Int, message: String) {
_out foreach {
_.sendMessage(messageType.value, message)
_.sendMessage(messageType, message)
}
}

Expand All @@ -90,8 +96,8 @@ object SocketIOSupport {
}
}

def broadcast(message: String) {
clients foreach { cl => if (cl != this) cl.send(message) }
def broadcast(messageType: Int, message: String) {
if(_broadcaster != null) _broadcaster(messageType, message)
}

def close() {
Expand Down Expand Up @@ -154,9 +160,11 @@ trait SocketIOSupport extends Handler with Initializable {
} else {
transport.handle(req, res, new Transport.InboundFactory {
def getInbound(p1: HttpServletRequest, p2: Array[String]) = {
val client = _builder.result
val client = _builder.result { c => _connections.remove(_connections.indexOf(c)) }
_connections.add(client)
client.clients = _connections
client.broadcaster { (messageType, message) =>
_connections.filterNot(_.clientId == client.clientId).foreach { _.send(messageType, message) }
}
client
}
}, sessionManager)
Expand Down

0 comments on commit 40c66fd

Please sign in to comment.