Skip to content

Instantly share code, notes, and snippets.

@ogarrett
Last active June 26, 2020 16:16
Show Gist options
  • Save ogarrett/5044961 to your computer and use it in GitHub Desktop.
Save ogarrett/5044961 to your computer and use it in GitHub Desktop.
WebSockets TrafficScript library

This Riverbed TrafficScript library implements support for identification and modification of WebSockets traffic in Stingray Traffic Manager.

For more details and usage instructions, check out https://splash.riverbed.com/docs/DOC-1451

Tags: #Stingray #TrafficScript #WebSockets #splash.riverbed.com

# WebSockets protocol library
# 7 Feb 2013
#
# This library is not supported by Riverbed Technology (or by me).
# For community support, refer to splash.riverbed.com
#
# Capabilities:
#
# isWS(): returns true if current connection is a websockets
# request, and false if not (for example, is HTTP)
# getMethod(), getPath(), getRawURL(), getVersion(),
# getHeader( headername ): retrieve values from websocket client
# handshake
# setMethod(), setPath(), setVersion(),
# setHeader( headername, headervalue ): set values in websocket
# client handshake
#
# Usage:
#
# Import library into TrafficScript rule as follows:
#
# import WebSockets as ws;
#
# Use library functions as follows:
#
# if( !ws.isWS() ) pool.use( "web servers loopback" );
#
# $hh = ws.getHeader( "Host" );
# if( $hh == "echo.site.com" ) {
# ws.setPath( "/echo" );
# pool.use( "Echo servers" );
# }
#
# Assign the rules to a 'Generic Streaming' virtual server type, and
# arrange that the rules run at request processing, and only 'run once'.
sub isWS() {
if( !connection.data.get( "ws-init" ) ) initRequest();
return (getHeader( "Connection" ) == "Upgrade" && getHeader( "Upgrade" ) == "websocket" );
}
sub getMethod() {
if( !connection.data.get( "ws-init" ) ) initRequest();
return connection.data.get( "ws-METHOD" );
}
sub getPath() {
if( !connection.data.get( "ws-init" ) ) initRequest();
return connection.data.get( "ws-PATH" );
}
sub getRawURL() {
if( !connection.data.get( "ws-init" ) ) initRequest();
return connection.data.get( "ws-RAWURL" );
}
sub getVersion() {
if( !connection.data.get( "ws-init" ) ) initRequest();
return connection.data.get( "ws-VERSION" );
}
sub getTerminator() {
if( !connection.data.get( "ws-init" ) ) initRequest();
return connection.data.get( "ws-TERMINATOR" );
}
sub getHeaders() {
if( !connection.data.get( "ws-init" ) ) initRequest();
return connection.data.get( "ws-HEADERS" );
}
sub getHeader( $h ) {
$t = getTerminator();
$hh = getHeaders();
string.regexMatch( $hh, $t.$h.':\s+(.*?)'.$t, "i" );
return $1;
}
sub setMethod( $method ) {
if( !connection.data.get( "ws-init" ) ) initRequest();
connection.data.set( "ws-METHOD", $method );
updateRequest();
}
sub setPath( $path ) {
if( !connection.data.get( "ws-init" ) ) initRequest();
connection.data.set( "ws-RAWURL", escape( $path ) );
connection.data.set( "ws-PATH", $path );
updateRequest();
}
sub setVersion( $version ) {
if( !connection.data.get( "ws-init" ) ) initRequest();
connection.data.set( "ws-VERSION", $version );
updateRequest();
}
sub setHeader( $h, $v ) {
if( !connection.data.get( "ws-init" ) ) initRequest();
$t = getTerminator();
$hh = getHeaders();
$hh2 = string.regexsub( $hh, $t.$h.':\s+.*?'.$t, $t.$h.': '.$v.$t );
if( $hh2 == $hh ) $hh2 = $t.$h.': '.$v.$hh;
connection.data.set( "ws-HEADERS", $hh2 );
updateRequest();
}
sub initRequest() {
$first = request.getLine();
$t = "\n";
if( endsWith( $first, "\r\n" ) ) $t = "\r\n";
connection.data.set( "ws-TERMINATOR", $t );
# Split the request to only get headers on the 1st request
request.endsWith( $t.$t );
# Headers are prefixed by a terminator to make searches easier, and
# end with the double-terminator
$headers = request.getLine( $t.$t, $1-length( $t ) );
# Parse the first line (best effort):
string.regexmatch( $first, '([^\s]+)\s(.+)\s(HTTP.*?)\s', "i" );
connection.data.set( "ws-METHOD", $1 );
connection.data.set( "ws-RAWURL", string.trim( $2 ) );
connection.data.set( "ws-PATH", string.unescape( string.trim( $2 ) ) );
connection.data.set( "ws-VERSION", $3 );
connection.data.set( "ws-HEADERS", $headers );
connection.data.set( "ws-init", 1 );
}
sub updateRequest() {
request.set( connection.data.get( "ws-METHOD" ) . " " .
connection.data.get( "ws-RAWURL" ) . " " .
connection.data.get( "ws-VERSION" ) .
connection.data.get( "ws-HEADERS" ) );
}
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<title>WebSockets Client</title>
</head>
<body>
<script language="javascript" type="text/javascript">
var websocket;
function init() {
$('#connect').click(function(){
doConnect();
});
$('#send').click(function() {
doSend( $('#text').val() );
});
$('#disconnect').click(function(){
websocket.close();
});
}
function doConnect() {
websocket = new WebSocket( $('#server').val() );
websocket.onopen = function( e ) {
writeToScreen("CONNECTED to " + websocket.url);
};
websocket.onclose = function( e ) {
writeToScreen("DISCONNECTED from " + websocket.url);
};
websocket.onmessage = function( e ) {
writeToScreen('<span style="color: blue;">&lt;--: ' + e.data + '</span>');
};
websocket.onerror = function( e ) {
writeToScreen('<span style="color: red;">ERROR:</span> ' + e.data);
};
}
function doSend(message) {
writeToScreen("--&gt;: " + message); websocket.send(message);
}
function writeToScreen(message) {
var pre = document.createElement("div");
pre.innerHTML = message;
$('#output').append(pre);
}
$(document).ready( init );
</script>
<h2>WebSocket Test</h2>
<!-- sample servers:
ws://echo.websocket.org/
wss://echo.websocket.org/
ws://ajf.me:8080/
-->
<input id="server" type="text" value="ws://echo.websocket.org/echo" size="36"/>
<button id="connect">Connect</button>
<br/>
<input id="text" type="text" />
<button id="send">Send</button>
<button id="disconnect">Disconnect</button>
<br/>
<br/>
<div id="output"></div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment