Skip to content

Instantly share code, notes, and snippets.

@minimal
Created January 20, 2010 01:17
Show Gist options
  • Save minimal/281490 to your computer and use it in GitHub Desktop.
Save minimal/281490 to your computer and use it in GitHub Desktop.
Websockets with clojure + jetty
<html><head>
<title>WebSocket Chat</title>
<!--If no native websockets use http://github.com/gimite/web-socket-js-->
<script type="text/javascript" src="swfobject.js"></script>
<script type="text/javascript" src="FABridge.js"></script>
<script type="text/javascript" src="web_socket.js"></script>
<script type='text/javascript'>
if (!window.WebSocket)
alert("WebSocket not supported by this browser");
// Set URL of your WebSocketMain.swf here:
WebSocket.__swfLocation = "WebSocketMain.swf";
function $() { return document.getElementById(arguments[0]); }
function $F() { return document.getElementById(arguments[0]).value; }
function getKeyCode(ev) { if (window.event) return window.event.keyCode; return ev.keyCode; }
var room = {
join: function(name) {
this._username=name;
//var location = document.location.toString().replace('http:','ws:');
var location = "ws://theoval:8090/";
this._ws=new WebSocket(location);
this._ws.onopen=this._onopen;
this._ws.onmessage=this._onmessage;
this._ws.onclose=this._onclose;
},
_onopen: function(){
$('join').className='hidden';
$('joined').className='';
$('phrase').focus();
room._send(room._username,'has joined!');
},
_send: function(user,message){
user=user.replace(':','_');
if (this._ws)
this._ws.send(user+':'+message);
},
chat: function(text) {
if (text != null && text.length>0 )
room._send(room._username,text);
},
_onmessage: function(m) {
if (m.data){
var c=m.data.indexOf(':');
var from=m.data.substring(0,c).replace('<','&lt;').replace('>','&gt;');
var text=m.data.substring(c+1).replace('<','&lt;').replace('>','&gt;');
var chat=$('chat');
var spanFrom = document.createElement('span');
spanFrom.className='from';
spanFrom.innerHTML=from+':&nbsp;';
var spanText = document.createElement('span');
spanText.className='text';
spanText.innerHTML=text;
var lineBreak = document.createElement('br');
chat.appendChild(spanFrom);
chat.appendChild(spanText);
chat.appendChild(lineBreak);
chat.scrollTop = chat.scrollHeight - chat.clientHeight;
}
},
_onclose: function(m) {
this._ws=null;
$('join').className='';
$('joined').className='hidden';
$('username').focus();
$('chat').innerHTML='';
}
};
</script>
<style type='text/css'>
div { border: 0px solid black; }
div#chat { clear: both; width: 40em; height: 20ex; overflow: auto; background-color: #f0f0f0; padding: 4px; border: 1px solid black; }
div#input { clear: both; width: 40em; padding: 4px; background-color: #e0e0e0; border: 1px solid black; border-top: 0px }
input#phrase { width:30em; background-color: #e0f0f0; }
input#username { width:14em; background-color: #e0f0f0; }
div.hidden { display: none; }
span.from { font-weight: bold; }
span.alert { font-style: italic; }
</style>
</head><body>
<div id='chat'></div>
<div id='input'>
<div id='join' >
Username:&nbsp;<input id='username' type='text'/><input id='joinB' class='button' type='submit' name='join' value='Join'/>
</div>
<div id='joined' class='hidden'>
Chat:&nbsp;<input id='phrase' type='text'/>
<input id='sendB' class='button' type='submit' name='join' value='Send'/>
</div>
</div>
<script type='text/javascript'>
$('username').setAttribute('autocomplete','OFF');
$('username').onkeyup = function(ev) { var keyc=getKeyCode(ev); if (keyc==13 || keyc==10) { room.join($F('username')); return false; } return true; } ;
$('joinB').onclick = function(event) { room.join($F('username')); return false; };
$('phrase').setAttribute('autocomplete','OFF');
$('phrase').onkeyup = function(ev) { var keyc=getKeyCode(ev); if (keyc==13 || keyc==10) { room.chat($F('phrase')); $('phrase').value=''; return false; } return true; };
$('sendB').onclick = function(event) { room.chat($F('phrase')); $('phrase').value=''; return false; };
</script>
<p>
This is a demonstration of the Jetty websocket server.
</p>
</body></html>
;; Copyright (c) James Reeves. All rights reserved.
;; The use and distribution terms for this software are covered by the Eclipse
;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which
;; can be found in the file epl-v10.html at the root of this distribution. By
;; using this software in any fashion, you are agreeing to be bound by the
;; terms of this license. You must not remove this notice, or any other, from
;; this software.
(ns compojure.server.jetty
"Clojure interface to start an embedded Jetty server."
(:use compojure.control)
(:use compojure.server.common)
(:import org.eclipse.jetty.server.Server)
(:import org.eclipse.jetty.servlet.ServletContextHandler)
(:import org.eclipse.jetty.servlet.ServletHolder)
(:import org.eclipse.jetty.server.bio.SocketConnector)
(:import org.eclipse.jetty.server.ssl.SslSocketConnector))
(defn servlet-holder
"Wrap a servlet in a ServletHolder object with a supplied set of parameters
to be set on servlet init."
[servlet & params]
(let [holder (new ServletHolder servlet)
params (partition 2 params)]
(doseq [[key val] params]
(.setInitParameter holder (name key) (str val)))
holder))
(defn get-context
"Get a Context instance for a server and hostname."
([server]
(get-context server nil))
([server host]
(let [context (ServletContextHandler. server "/" ServletContextHandler/SESSIONS)]
(if host
(doto context (.setVirtualHosts (into-array [host])))
context))))
(decorate-with memoize get-context)
(defn add-servlet!
"Add a servlet to a Jetty server. Servlets can be connected to a relative
path or an absolute URL. When connected to a URL, the function will try and
use the hostname to set up a virtual host. Wildcards for the domain and path
are allowed."
[server url-or-path servlet]
(prn (class servlet))
(let [[host path] (get-host-and-path url-or-path)
context (get-context server host)
holder (if (instance? ServletHolder servlet)
servlet
(ServletHolder. servlet))]
(.addServlet context holder path)))
(defn- add-ssl-connector!
"Add an SslSocketConnector to a Jetty server."
[server options]
(let [ssl-connector (SslSocketConnector.)]
(doto ssl-connector
(.setPort (options :ssl-port 443))
(.setKeystore (options :keystore))
(.setKeyPassword (options :key-password)))
(when (options :truststore)
(.setTruststore ssl-connector (options :truststore)))
(when (options :trust-password)
(.setTrustPassword ssl-connector (options :trust-password)))
(.addConnector server ssl-connector)))
(defn- create-server
"Construct a Jetty Server instance."
[options servlets]
(let [connector (doto (SocketConnector.)
(.setPort (options :port 80))
(.setHost (options :host)))
server (doto (Server.)
(.addConnector connector))
servlets (partition 2 servlets)]
(when (or (options :ssl) (options :ssl-port))
(add-ssl-connector! server options))
(doseq [[url-or-path servlet] servlets]
(add-servlet! server url-or-path servlet))
server))
(defn jetty-server
"Create a new Jetty HTTP server with the supplied options and servlets."
[options? & servlets]
(server-with-options create-server options? servlets))
(defmacro defserver
"Shortcut for (def name (http-server args))"
[name & args]
`(def ~name (jetty-server ~@args)))
(defn start "Start a HTTP server."
[server]
(.start server))
(defn stop "Stop a HTTP server."
[server]
(.stop server))
(defn run-server
"Create and start a new Jetty HTTP server."
[& server-args]
(let [server (apply jetty-server server-args)]
(.start server)
server))
;; trying to test jetty websockets in clojure
;; http://blogs.webtide.com/gregw/entry/jetty_websocket_server
;; To do basic test in chrome:
;; connect to host:8080
;; open java console
;; _ws=new WebSocket("ws://192.168.1.17:8080")
;; _ws.onmessage = function(m){console.log("Recieved websocket message: " + m.data);}
;; _ws.send('Hello from websocket!!', '')
(ns websockettest
(:use [compojure])
(:import java.io.IOException
;;java.util.Set
;;java.util.concurrent.CopyOnWriteArraySet
javax.servlet.RequestDispatcher
(javax.servlet.http
HttpServletRequest
HttpServletResponse)
org.eclipse.jetty.util.TypeUtil
org.eclipse.jetty.util.log.Log
(org.eclipse.jetty.websocket
WebSocket
WebSocketServlet)))
(def members (atom #{})) ;; set of member objects
(def outbounds (atom {}))
(defn sendmsgs
[frame data members]
(loop [col members]
(if (empty? col)
nil
(do (println "member: " (first col))
(.sendMessage (last (first col)) frame data)
(recur (rest col))))))
(defn make-chat-websock []
(let [state (atom 0)
obj (proxy [WebSocket] []
(onConnect [outbound]
(swap! outbounds assoc this outbound)
(swap! members conj this))
(onMessage [frame data]
(do
(println "recieved: " data)
(sendmsgs frame data @outbounds)))
(onDisconnect []
(swap! outbounds dissoc this)))]
obj))
(defn web-sock-serv []
(proxy [WebSocketServlet] []
(doGet [request response]
;; (let [context (proxy-super getServletContext)
;; _ (println "Context: " context)
;; _ (println "name" (proxy-super getServletName))
;; _ (println "hekki" (.getNamedDispatcher context (proxy-super getServletName) ))])
(.. (proxy-super getServletContext)
(getNamedDispatcher (proxy-super getServletName))
(forward request response)))
(doWebSocketConnect [request response]
(make-chat-websock))))
(defn do-run-server []
(run-server {:port 8090}
"/*" (web-sock-serv)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment