|
# 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" ) ); |
|
} |