Created
January 20, 2010 01:17
-
-
Save minimal/281490 to your computer and use it in GitHub Desktop.
Websockets with clojure + jetty
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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('<','<').replace('>','>'); | |
var text=m.data.substring(c+1).replace('<','<').replace('>','>'); | |
var chat=$('chat'); | |
var spanFrom = document.createElement('span'); | |
spanFrom.className='from'; | |
spanFrom.innerHTML=from+': '; | |
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: <input id='username' type='text'/><input id='joinB' class='button' type='submit' name='join' value='Join'/> | |
</div> | |
<div id='joined' class='hidden'> | |
Chat: <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> | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; 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)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; 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