Skip to content

Instantly share code, notes, and snippets.

@motemen
Created October 18, 2010 15:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save motemen/632374 to your computer and use it in GitHub Desktop.
Save motemen/632374 to your computer and use it in GitHub Desktop.
use strict;
use warnings;
use Twiggy::Server;
use Plack::Request;
use AnyEvent::Handle;
use Digest::MD5 qw(md5);
my $server = Twiggy::Server->new(port => 9090);
my $DATA = do { local $/; <DATA> };
my $app = sub {
my $env = shift;
my $req = Plack::Request->new($env);
my $res = $req->new_response(200);
if ($req->path eq '/') {
$res->content_type('text/html; charset=utf-8');
(my $data = $DATA) =~ s/{{{HOST}}}/$env->{HTTP_HOST}/g;
$res->content($data);
}
elsif ($req->path eq '/echo') {
unless ($env->{HTTP_CONNECTION} eq 'Upgrade' && $env->{HTTP_UPGRADE} eq 'WebSocket') {
$res->code(400);
return $res->finalize;
}
return sub {
my $respond = shift;
my $hh = join "\015\012", (
'HTTP/1.1 101 Web Socket Protocol Handshake',
'Upgrade: WebSocket',
'Connection: Upgrade',
"Sec-WebSocket-Origin: $env->{HTTP_ORIGIN}",
"Sec-WebSocket-Location: ws://$env->{HTTP_HOST}$env->{SCRIPT_NAME}$env->{PATH_INFO}",
'',
);
my $fh = $env->{'psgix.io'}
or return $respond->([
501, [ 'Content-Type', 'text/plain' ], [ 'This server does not support psgix.io extension' ]
]);
my $key1 = $env->{'HTTP_SEC_WEBSOCKET_KEY1'};
my $key2 = $env->{'HTTP_SEC_WEBSOCKET_KEY2'};
my $n1 = join '', $key1 =~ /\d+/g;
my $n2 = join '', $key2 =~ /\d+/g;
my $s1 = $key1 =~ y/ / /;
my $s2 = $key2 =~ y/ / /;
$n1 = int($n1 / $s1);
$n2 = int($n2 / $s2);
my $len = read $fh, my $chunk, 8;
unless (defined $len) {
return $respond->([
500, [ 'Content-Type', 'text/plain' ], [ "read: $!" ]
]);
}
my $string = pack('N', $n1) . pack('N', $n2) . $chunk;
my $digest = md5 $string;
my $h = AnyEvent::Handle->new(fh => $fh);
$h->push_write($hh);
$h->push_write("\015\012");
$h->push_write("$digest");
$h->on_error(sub {
warn "error: @_";
undef $h;
});
$h->on_read(sub {
$_[0]->push_read(
line => "\xff",
sub {
my ($h, $line) = @_;
$line =~ s/^\0// or warn;
$h->push_write("\x00$line\xff");
}
);
});
$h->on_eof(sub {
warn 'eof';
undef $h;
});
};
}
else {
$res->code(404);
}
$res->finalize;
};
$server->register_service($app);
AE::cv->recv;
__DATA__
<!DOCTYPE html>
<html>
<head>
<title>WebSocket</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<style type="text/css">
#log {
border: 1px solid #DDD;
padding: 0.5em;
}
</style>
</head>
<body>
<script type="text/javascript">
function log (msg) {
$('#log').text($('#log').text() + msg + "\n");
}
$(function () {
var ws = new WebSocket('ws://{{{HOST}}}/echo');
log('WebSocket start');
ws.onopen = function () {
log('connected');
};
ws.onmessage = function (ev) {
log('received: ' + ev.data);
$('#message').val('');
};
ws.onerror = function (ev) {
log('error: ' + ev.data);
}
ws.onclose = function (ev) {
log('closed');
}
$('#form').submit(function () {
ws.send($('#message').val());
return false;
});
});
</script>
<form id="form">
<input type="text" name="message" id="message" />
<input type="submit" />
</form>
<pre id="log"></pre>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment