[Slim-Checkins] r12600 - in /trunk/server/Slim: Control/Queries.pm Control/Request.pm Utils/Log.pm Web/Cometd.pm Web/Cometd/ Web/Cometd/Manager.pm Web/HTTP.pm Web/HTTP/ Web/HTTP/ClientConn.pm Web/JSONRPC.pm
andy at svn.slimdevices.com
andy at svn.slimdevices.com
Sat Aug 18 14:21:45 PDT 2007
Author: andy
Date: Sat Aug 18 14:21:45 2007
New Revision: 12600
URL: http://svn.slimdevices.com?rev=12600&view=rev
Log:
Cometd server implementation, this will be used by Jive, and could be used by skins as well via Dojo's built-in comet support. Currently it is not well tested and probably buggy.
Added:
trunk/server/Slim/Web/Cometd/
trunk/server/Slim/Web/Cometd.pm
trunk/server/Slim/Web/Cometd/Manager.pm
trunk/server/Slim/Web/HTTP/
trunk/server/Slim/Web/HTTP/ClientConn.pm
Modified:
trunk/server/Slim/Control/Queries.pm
trunk/server/Slim/Control/Request.pm
trunk/server/Slim/Utils/Log.pm
trunk/server/Slim/Web/HTTP.pm
trunk/server/Slim/Web/JSONRPC.pm
Modified: trunk/server/Slim/Control/Queries.pm
URL: http://svn.slimdevices.com/trunk/server/Slim/Control/Queries.pm?rev=12600&r1=12599&r2=12600&view=diff
==============================================================================
--- trunk/server/Slim/Control/Queries.pm (original)
+++ trunk/server/Slim/Control/Queries.pm Sat Aug 18 14:21:45 2007
@@ -794,7 +794,7 @@
}
}
- } elsif ($source eq 'JIV') {
+ } elsif ( $source =~ m{^(?:JSONRPC|/)} ) { # Return this for JSONRPC or Cometd requests (start with /)
# send display to jive from one of the following components
if (my $ref = $parts->{'jiv'} && ref $parts->{'jiv'}) {
Modified: trunk/server/Slim/Control/Request.pm
URL: http://svn.slimdevices.com/trunk/server/Slim/Control/Request.pm?rev=12600&r1=12599&r2=12600&view=diff
==============================================================================
--- trunk/server/Slim/Control/Request.pm (original)
+++ trunk/server/Slim/Control/Request.pm Sat Aug 18 14:21:45 2007
@@ -860,6 +860,7 @@
'_ae_callback' => undef,
'_ae_filter' => undef,
'_private' => undef,
+ '_disableTied' => 0,
};
bless $self, $class;
@@ -870,6 +871,17 @@
$self->validate();
return $self;
+}
+
+# Disable tied hashes
+sub disableTiedHashes {
+ my $self = shift;
+
+ $self->{_disableTied} = 1;
+
+ # Copy tied hashes back into normal hashes
+ $self->{_params} = { %{ $self->{_params} } };
+ $self->{_results} = { %{ $self->{_results} } };
}
# makes a request out of another one, discarding results and callback data.
@@ -890,6 +902,7 @@
$copy->{'_ae_callback'} = $self->{'_ae_callback'};
$copy->{'_ae_filter'} = $self->{'_ae_filter'};
$copy->{'_curparam'} = $self->{'_curparam'};
+ $copy->{'_disableTied'} = $self->{'_disableTied'};
# duplicate the arrays and hashes
my @request = @{$self->{'_request'}};
@@ -1280,7 +1293,12 @@
sub getParamsCopy {
my $self = shift;
- tie (my %paramHash, "Tie::IxHash");
+ my %paramHash;
+
+ if ( !$self->{'_disableTied'} ) {
+ tie %paramHash, 'Tie::IxHash';
+ }
+
while (my ($key, $val) = each %{$self->{'_params'}}) {
$paramHash{$key} = $val;
}
@@ -1330,7 +1348,11 @@
}
if (!defined ${$self->{'_results'}}{$loop}->[$loopidx]) {
- tie (my %paramHash, "Tie::IxHash");
+ my %paramHash;
+ if ( !$self->{'_disableTied'} ) {
+ tie %paramHash, 'Tie::IxHash';
+ }
+
${$self->{'_results'}}{$loop}->[$loopidx] = \%paramHash;
}
@@ -1418,6 +1440,12 @@
}
}
+sub getResults {
+ my $self = shift;
+
+ return $self->{'_results'};
+}
+
sub getResult {
my $self = shift;
my $key = shift || return;
@@ -1467,7 +1495,11 @@
sub cleanResults {
my $self = shift;
- tie (my %resultHash, "Tie::IxHash");
+ my %resultHash;
+
+ if ( !$self->{'_disableTied'} ) {
+ tie %resultHash, 'Tie::IxHash';
+ }
# not sure this helps release memory, but can't hurt
delete $self->{'_results'};
Modified: trunk/server/Slim/Utils/Log.pm
URL: http://svn.slimdevices.com/trunk/server/Slim/Utils/Log.pm?rev=12600&r1=12599&r2=12600&view=diff
==============================================================================
--- trunk/server/Slim/Utils/Log.pm (original)
+++ trunk/server/Slim/Utils/Log.pm Sat Aug 18 14:21:45 2007
@@ -744,6 +744,7 @@
'network.upnp' => 'WARN',
'network.jsonrpc' => 'WARN',
'network.squeezenetwork' => 'WARN',
+ 'network.cometd' => 'WARN',
'formats.audio' => 'WARN',
'formats.xml' => 'WARN',
Added: trunk/server/Slim/Web/Cometd.pm
URL: http://svn.slimdevices.com/trunk/server/Slim/Web/Cometd.pm?rev=12600&view=auto
==============================================================================
--- trunk/server/Slim/Web/Cometd.pm (added)
+++ trunk/server/Slim/Web/Cometd.pm Sat Aug 18 14:21:45 2007
@@ -1,0 +1,567 @@
+package Slim::Web::Cometd;
+
+# $Id$
+
+# SlimServer Copyright (c) 2001-2007 Logitech.
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License,
+# version 2.
+
+# This class provides an implementation of the Cometd Bayeux protocol
+# The primary purpose is for handling Jive connections, but it may also
+# be used in the future for real-time updates to the web interface.
+#
+# Much of this code is thanks to David Davis' cometd-perl implementation.
+#
+# Current protocol documentation is available at
+# http://svn.xantus.org/shortbus/trunk/bayeux/bayeux.html
+
+use strict;
+
+use bytes;
+use Data::UUID;
+use Digest::SHA1 qw(sha1_hex);
+use HTTP::Date;
+use JSON::XS qw(to_json from_json);
+use Scalar::Util qw(blessed);
+use URI::Escape qw(uri_unescape);
+
+use Slim::Control::Request;
+use Slim::Web::Cometd::Manager;
+use Slim::Web::HTTP;
+use Slim::Utils::Log;
+use Slim::Utils::Timers;
+
+my $log = logger('network.cometd');
+
+my $uuid = Data::UUID->new;
+
+my $manager = Slim::Web::Cometd::Manager->new;
+
+use constant PROTOCOL_VERSION => '1.0';
+use constant HASH_KEY => 'sl1ms3rv3r';
+use constant RETRY_DELAY => 5000;
+
+sub init {
+ Slim::Web::HTTP::addRawFunction( '/cometd', \&handler );
+ Slim::Web::HTTP::addCloseHandler( \&closeHandler );
+}
+
+sub handler {
+ my ( $httpClient, $httpResponse ) = @_;
+
+ # make sure we're connected
+ if ( !$httpClient->connected ) {
+ $log->warn("Aborting, client not connected: $httpClient");
+ return;
+ }
+
+ my $req = $httpResponse->request;
+ my $ct = $req->content_type;
+
+ my ( $params, %ops );
+
+ if ( $ct && $ct eq 'text/json' ) {
+ # POST
+ if ( my $content = $req->content ) {
+ $ops{message} = $content;
+ }
+ }
+ elsif ( $ct && $ct eq 'application/x-www-form-urlencoded' ) {
+ # POST or GET
+ if ( my $content = $req->content ) {
+ $params = $content;
+ }
+ elsif ( $req->uri =~ m{\?message=} ) {
+ $params = ( $req->uri =~ m{\?(.*)} )[ 0 ];
+ }
+ }
+
+ if ( $params && $params =~ m{=} ) {
+ # uri param ?message=[json]
+ %ops = map {
+ my ( $k, $v ) = split( '=' );
+ uri_unescape( $k ) => uri_unescape( $v )
+ } split( '&', $params );
+ }
+ elsif ( $params ) {
+ # uri param ?[json]
+ $ops{message} = $params;
+ }
+
+ if ( !$ops{message} ) {
+ sendResponse(
+ $httpClient,
+ $httpResponse,
+ [ { successful => JSON::XS::false, error => 'no bayeux message found' } ]
+ );
+ return;
+ }
+
+ my $objs = eval { from_json( $ops{message} ) };
+ if ( $@ ) {
+ sendResponse(
+ $httpClient,
+ $httpResponse,
+ [ { successful => JSON::XS::false, error => "$@" } ]
+ );
+ return;
+ }
+
+ if ( ref $objs ne 'ARRAY' ) {
+ sendResponse(
+ $httpClient,
+ $httpResponse,
+ [ { successful => JSON::XS::false, error => 'bayeux message not an array' } ]
+ );
+ return;
+ }
+
+ if ( $log->is_debug ) {
+ $log->debug( "Cometd request: " . Data::Dump::dump( $objs ) );
+ }
+
+ my $clid;
+ my $events = [];
+ my @errors;
+
+ for my $obj ( @{$objs} ) {
+ if ( ref $obj ne 'HASH' ) {
+ sendResponse(
+ $httpClient,
+ $httpResponse,
+ [ { successful => JSON::XS::false, error => 'bayeux event not a hash' } ]
+ );
+ return;
+ }
+
+ if ( !$clid ) {
+ # specified clientId and authToken
+ if ( $obj->{clientId} ) {
+ $clid = $obj->{clientId};
+ }
+ elsif ( $obj->{channel} eq '/meta/handshake' ) {
+ $clid = lc( $uuid->create_hex );
+ $manager->register_clid( $clid );
+ }
+ else {
+ push @errors, [ $obj->{channel}, 'clientId not supplied' ];
+ }
+
+ # Register client with HTTP connection
+ if ( $clid ) {
+ $httpClient->clid( $clid );
+ }
+ }
+
+ last if @errors;
+
+ if ( $obj->{channel} eq '/meta/handshake' ) {
+
+ push @{$events}, {
+ channel => '/meta/handshake',
+ version => PROTOCOL_VERSION,
+ supportedConnectionTypes => [ 'long-polling', 'streaming' ],
+ clientId => $clid,
+ successful => JSON::XS::true,
+ advice => {
+ reconnect => 'retry', # one of "none", "retry", "handshake", "recover"
+ interval => RETRY_DELAY, # retry delay in ms
+ },
+ };
+ }
+ elsif ( $obj->{channel} eq '/meta/connect' ) {
+
+ if ( !$manager->is_valid_clid( $clid ) ) {
+ # Invalid clientId, send advice to re-handshake
+
+ push @{$events}, {
+ channel => '/meta/connect',
+ clientId => undef,
+ successful => JSON::XS::false,
+ timestamp => time2str( time() ),
+ error => 'invalid clientId',
+ advice => {
+ reconnect => 'handshake',
+ interval => 0,
+ }
+ };
+ }
+ else {
+ # Valid clientId
+
+ push @{$events}, {
+ channel => '/meta/connect',
+ clientId => $clid,
+ successful => JSON::XS::true,
+ timestamp => time2str( time() ),
+ };
+
+ # Add any additional pending events
+ push @{$events}, ( $manager->get_pending_events( $clid ) );
+
+ if ( $obj->{connectionType} eq 'streaming' ) {
+ # Streaming connections use chunked transfer encoding
+ $httpResponse->header( 'Transfer-Encoding' => 'chunked' );
+
+ # Tell HTTP client our transport
+ $httpClient->transport( 'streaming' );
+
+ # Tell the manager about the streaming connection
+ $manager->register_streaming_connection(
+ $clid, $httpClient, $httpResponse
+ );
+ }
+ else {
+ $httpClient->transport( 'polling' );
+
+ # XXX: todo
+ }
+ }
+ }
+ elsif ( $obj->{channel} eq '/meta/reconnect' ) {
+
+ if ( !$manager->is_valid_clid( $clid ) ) {
+ # Invalid clientId, send advice to recover
+
+ push @{$events}, {
+ channel => '/meta/reconnect',
+ successful => JSON::XS::false,
+ timestamp => time2str( time() ),
+ error => 'invalid clientId',
+ advice => {
+ reconnect => 'recover',
+ interval => 0,
+ }
+ };
+ }
+ else {
+ # Valid clientId, reconnect them
+
+ $log->debug( "Client reconnected: $clid" );
+
+ push @{$events}, {
+ channel => '/meta/reconnect',
+ successful => JSON::XS::true,
+ timestamp => time2str( time() ),
+ };
+
+ # Add any additional pending events
+ push @{$events}, ( $manager->get_pending_events( $clid ) );
+
+ # Remove disconnect timer
+ Slim::Utils::Timers::killTimers( $clid, \&disconnectClient );
+
+ if ( $obj->{connectionType} eq 'streaming' ) {
+ # Streaming connections use chunked transfer encoding
+ $httpResponse->header( 'Transfer-Encoding' => 'chunked' );
+
+ # Tell HTTP client our transport
+ $httpClient->transport( 'streaming' );
+
+ # Tell the manager about the streaming connection
+ $manager->register_streaming_connection(
+ $clid, $httpClient, $httpResponse
+ );
+ }
+ else {
+ $httpClient->transport( 'polling' );
+
+ # XXX: todo
+ }
+ }
+ }
+ elsif ( $obj->{channel} eq '/meta/disconnect' ) {
+
+ if ( !$manager->is_valid_clid( $clid ) ) {
+ # Invalid clientId, send error
+
+ push @{$events}, {
+ channel => '/meta/disconnect',
+ clientId => undef,
+ successful => JSON::XS::false,
+ error => 'invalid clientId',
+ };
+ }
+ else {
+ # Valid clientId, disconnect them
+
+ push @{$events}, {
+ channel => '/meta/disconnect',
+ clientId => $clid,
+ successful => JSON::XS::true,
+ timestamp => time2str( time() ),
+ };
+
+ # Close the connection after this response
+ $httpResponse->header( Connection => 'close' );
+
+ disconnectClient( $clid );
+ }
+ }
+ elsif ( $obj->{channel} eq '/meta/subscribe' ) {
+
+ # We expect all our subscribe events to contain 'ext'
+ # values that correspond to requests
+ my $request = $obj->{ext}->{'slim.request'};
+ my $subscription = $obj->{subscription};
+
+ if ( $request && $subscription ) {
+ my $result = handleRequest( $clid, $request, $obj->{channel}, $subscription );
+
+ if ( $result->{error} ) {
+ push @errors, [ $obj->{channel}, $result->{error} ];
+ }
+ else {
+ push @{$events}, {
+ channel => '/meta/subscribe',
+ clientId => $clid,
+ successful => JSON::XS::true,
+ ext => $obj->{ext},
+ };
+
+ # If the request was not async, we can add it now
+ if ( exists $result->{data} ) {
+ push @{$events}, $result;
+ }
+ }
+ }
+ else {
+ if ( !$request ) {
+ push @errors, [ $obj->{channel}, 'slim.request ext key not found' ];
+ }
+ elsif ( !$subscription ) {
+ push @errors, [ $obj->{channel}, 'subscription key not found' ];
+ }
+ }
+ }
+ elsif ( $obj->{channel} eq '/slim/request' ) {
+
+ # A non-subscription request
+ my $request = $obj->{data};
+ my $id = $obj->{id} || lc( $uuid->create_hex ); # unique id for this request
+
+ if ( $request && $id ) {
+ my $result = handleRequest( $clid, $request, $obj->{channel}, $id );
+
+ if ( $result->{error} ) {
+ push @errors, [ $obj->{channel}, $result->{error} ];
+ }
+ else {
+ # This response is optional, but we do it anyway
+ push @{$events}, {
+ channel => '/slim/request',
+ clientId => $clid,
+ id => $id,
+ successful => JSON::XS::true,
+ ext => $obj->{data},
+ };
+
+ # If the request was not async, we can add it now
+ if ( exists $result->{data} ) {
+ push @{$events}, $result;
+ }
+ }
+ }
+ }
+ }
+
+ if ( @errors ) {
+ my $out = [];
+
+ for my $error ( @errors ) {
+ push @{$out}, {
+ channel => $error->[0],
+ successful => JSON::XS::false,
+ error => $error->[1],
+ };
+ }
+
+ sendResponse(
+ $httpClient, $httpResponse, $out,
+ );
+
+ return;
+ }
+
+ sendResponse(
+ $httpClient, $httpResponse, $events,
+ );
+}
+
+sub sendResponse {
+ my ( $httpClient, $httpResponse, $out ) = @_;
+
+ $httpResponse->code( 200 );
+ $httpResponse->header( Expires => '-1' );
+ $httpResponse->header( Pragma => 'no-cache' );
+ $httpResponse->header( 'Cache-Control' => 'no-cache' );
+ $httpResponse->header( 'Content-Type' => 'application/json' );
+
+ $out = eval { to_json( $out ) };
+ if ( $@ ) {
+ $out = to_json( [ { successful => JSON::XS::false, error => "$@" } ] );
+ }
+
+ my $sendheaders = 1; # should we send headers?
+ my $chunked = 0; # is this a chunked connection?
+
+ if ( $httpResponse->header('Transfer-Encoding') ) {
+ $chunked = 1;
+
+ # Have we already sent headers on this connection?
+ if ( $httpClient->sent_headers ) {
+ $sendheaders = 0;
+ }
+ else {
+ $httpClient->sent_headers(1);
+ }
+ }
+ else {
+ $httpResponse->header( 'Content-Length', length $out );
+ $sendheaders = 1;
+ }
+
+ if ( $log->is_debug ) {
+ if ( $sendheaders ) {
+ $log->debug( "Sending Cometd Response:\n"
+ . $httpResponse->as_string . $out
+ );
+ }
+ else {
+ $log->debug( "Sending Cometd chunk:\n" . $out );
+ }
+ }
+
+ Slim::Web::HTTP::addHTTPResponse(
+ $httpClient, $httpResponse, \$out, $sendheaders, $chunked,
+ );
+}
+
+sub handleRequest {
+ my ( $clid, $params, $channel, $id ) = @_;
+
+ my $args = $params->[1];
+
+ if ( !$args || ref $args ne 'ARRAY' ) {
+ return { error => 'invalid slim.request arguments, array expected' };
+ }
+
+ my $clientid;
+
+ if ( my $mac = $params->[0] ) {
+ my $client = Slim::Player::Client::getClient($mac);
+ $clientid = blessed($client) ? $client->id : undef;
+ }
+
+ # create a request
+ my $request = Slim::Control::Request->new( $clientid, $args );
+
+ if ( $request->isStatusDispatchable ) {
+ # fix the encoding and/or manage charset param
+ $request->fixEncoding;
+
+ # We don't want tied hashes
+ $request->disableTiedHashes;
+
+ # remember channel, request id and client id
+ $request->source( "$channel|$id" );
+ $request->connectionID( $clid );
+
+ $request->autoExecuteCallback( \&requestCallback );
+
+ $request->execute();
+
+ if ( $request->isStatusError ) {
+ return { error => 'request failed with error: ' . $request->getStatusText };
+ }
+
+ # handle async commands
+ if ( $request->isStatusProcessing ) {
+ $request->callbackParameters( \&requestCallback );
+
+ $log->debug( "Request for $channel / $id is async, will callback" );
+
+ return { ok => 1 };
+ }
+
+ # the request was successful and is not async
+ $log->debug( "Request for $channel / $id is not async" );
+
+ if ( $channel eq '/meta/subscribe' ) {
+ $channel = $id;
+ $id = undef;
+ }
+
+ return {
+ channel => $channel,
+ id => $id,
+ data => $request->getResults,
+ timestamp => time2str( time() ),
+ };
+ }
+ else {
+ return { error => 'invalid slim.request' };
+ }
+}
+
+sub requestCallback {
+ my $request = shift;
+
+ my $clid = $request->connectionID;
+ my ($channel, $id) = split /\|/, $request->source, 2;
+
+ $log->debug( "requestCallback got results for $clid / $channel / $id" );
+
+ if ( $channel eq '/meta/subscribe' ) {
+ $channel = $id;
+ $id = undef;
+ }
+
+ # Construct event response
+ my $events = [ {
+ channel => $channel,
+ id => $id,
+ data => $request->getResults,
+ timestamp => time2str( time() ),
+ } ];
+
+ # Deliver request results via Manager
+ $manager->deliver_events( $clid, $events );
+}
+
+sub closeHandler {
+ my $httpClient = shift;
+
+ # unregister connection from manager
+ my $clid = $httpClient->clid || return;
+
+ if ( $log->is_debug ) {
+ $log->debug( "Lost connection, clid: $clid, transport: " . $httpClient->transport );
+ }
+
+ $manager->unregister_connection( $clid, $httpClient );
+
+ Slim::Utils::Timers::setTimer(
+ $clid,
+ Time::HiRes::time() + ( ( RETRY_DELAY / 1000 ) * 2 ),
+ \&disconnectClient,
+ );
+}
+
+sub disconnectClient {
+ my $clid = shift;
+
+ # Clean up only if this client has no other connections
+ if ( $manager->is_valid_clid( $clid) && !$manager->has_connections( $clid ) ) {
+ $log->debug( "Disconnect for $clid, removing subscriptions" );
+
+ # Remove any subscriptions for this client
+ Slim::Control::Request::unregisterAutoExecute( $clid );
+
+ # Remove client from manager
+ $manager->remove_client( $clid );
+ }
+}
+
+1;
Added: trunk/server/Slim/Web/Cometd/Manager.pm
URL: http://svn.slimdevices.com/trunk/server/Slim/Web/Cometd/Manager.pm?rev=12600&view=auto
==============================================================================
--- trunk/server/Slim/Web/Cometd/Manager.pm (added)
+++ trunk/server/Slim/Web/Cometd/Manager.pm Sat Aug 18 14:21:45 2007
@@ -1,0 +1,138 @@
+package Slim::Web::Cometd::Manager;
+
+# $Id$
+
+# SlimServer Copyright (c) 2001-2007 Logitech.
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License,
+# version 2.
+
+# This class manages clients and subscriptions
+
+use strict;
+
+use Scalar::Util qw(weaken);
+
+use Slim::Utils::Log;
+use Slim::Utils::Timers;
+use Slim::Web::HTTP;
+
+my $log = logger('network.cometd');
+
+sub new {
+ my ( $class, %args ) = @_;
+
+ my $self = {
+ clients => {},
+ };
+
+ bless $self, ref $class || $class;
+}
+
+# Register a new clid created during handshake
+sub register_clid {
+ my ( $self, $clid ) = @_;
+
+ $self->{clients}->{$clid} = {
+ pending_events => {}, # stores most recent event per channel
+ streaming_connection => undef, # streaming connection to use
+ streaming_response => undef, # response object for streaming
+ };
+
+ return $clid;
+}
+
+sub is_valid_clid {
+ my ( $self, $clid ) = @_;
+
+ return exists $self->{clients}->{$clid};
+}
+
+sub remove_client {
+ my ( $self, $clid ) = @_;
+
+ delete $self->{clients}->{$clid};
+
+ return 1;
+}
+
+sub get_pending_events {
+ my ( $self, $clid ) = @_;
+
+ my $client = $self->{clients}->{$clid};
+
+ my $events = [];
+
+ while ( my ($subscription, $event) = each %{ $client->{pending_events} } ) {
+ push @{$events}, $event;
+ }
+
+ # Clear all pending events
+ $client->{pending_events} = {};
+
+ return wantarray ? @{$events} : $events;
+}
+
+sub register_streaming_connection {
+ my ( $self, $clid, $conn, $res ) = @_;
+
+ my $client = $self->{clients}->{$clid};
+
+ $client->{streaming_connection} = $conn;
+ $client->{streaming_response} = $res;
+}
+
+sub unregister_connection {
+ my ( $self, $clid, $conn ) = @_;
+
+ my $client = $self->{clients}->{$clid};
+
+ if ( $conn->transport eq 'streaming' ) {
+ if ( $client->{streaming_connection} eq $conn ) {
+ delete $client->{streaming_connection};
+ delete $client->{streaming_response};
+ }
+ }
+ elsif ( $conn->transport eq 'polling' ) {
+ # XXX: todo
+ }
+}
+
+sub has_connections {
+ my ( $self, $clid ) = @_;
+
+ my $client = $self->{clients}->{$clid};
+
+ return 1 if exists $client->{streaming_connection};
+
+ return 0;
+}
+
+sub deliver_events {
+ my ( $self, $clid, $events ) = @_;
+
+ my $client = $self->{clients}->{$clid};
+
+ my $conn = $client->{streaming_connection};
+ my $res = $client->{streaming_response};
+
+ if ( $conn && $res ) {
+ # If we have a streaming connection to send to...
+
+ # Prepend all queued events, if any
+ unshift @{$events}, ( $self->get_pending_events( $clid ) );
+
+ if ( $log->is_debug ) {
+ $log->debug(
+ "Delivering events to $clid:\n"
+ . Data::Dump::dump( $events )
+ );
+ }
+
+ Slim::Web::Cometd::sendResponse( $conn, $res, $events );
+ }
+
+ return 1;
+}
+
+1;
Modified: trunk/server/Slim/Web/HTTP.pm
URL: http://svn.slimdevices.com/trunk/server/Slim/Web/HTTP.pm?rev=12600&r1=12599&r2=12600&view=diff
==============================================================================
--- trunk/server/Slim/Web/HTTP.pm (original)
+++ trunk/server/Slim/Web/HTTP.pm Sat Aug 18 14:21:45 2007
@@ -41,9 +41,11 @@
use Slim::Utils::OSDetect;
use Slim::Utils::Strings qw(string);
use Slim::Utils::Unicode;
+use Slim::Web::HTTP::ClientConn;
use Slim::Web::Pages;
use Slim::Web::Graphics;
use Slim::Web::JSONRPC;
+use Slim::Web::Cometd;
use Slim::Utils::Prefs;
BEGIN {
@@ -152,6 +154,9 @@
# Initialize JSON RPC
Slim::Web::JSONRPC::init();
+
+ # Initialize Cometd
+ Slim::Web::Cometd::init();
}
sub init2 {
@@ -231,7 +236,7 @@
sub acceptHTTP {
# try and pull the handle
- my $httpClient = $http_server_socket->accept() || do {
+ my $httpClient = $http_server_socket->accept('Slim::Web::HTTP::ClientConn') || do {
$log->info("Did not accept connection, accept returned nothing");
return;
@@ -372,7 +377,7 @@
# socket half-closed from client
if (!defined $request) {
- $log->info("Client at $peeraddr{$httpClient} disconnected. (half-closed)");
+ $log->info("Client at $peeraddr{$httpClient}:" . $httpClient->peerport . " disconnected. (half-closed)");
closeHTTPSocket($httpClient);
return;
Added: trunk/server/Slim/Web/HTTP/ClientConn.pm
URL: http://svn.slimdevices.com/trunk/server/Slim/Web/HTTP/ClientConn.pm?rev=12600&view=auto
==============================================================================
--- trunk/server/Slim/Web/HTTP/ClientConn.pm (added)
+++ trunk/server/Slim/Web/HTTP/ClientConn.pm Sat Aug 18 14:21:45 2007
@@ -1,0 +1,42 @@
+package Slim::Web::HTTP::ClientConn;
+
+# $Id$
+
+# Subclass of HTTP::Daemon::ClientConn that represents a web client
+
+use strict;
+use base 'HTTP::Daemon::ClientConn';
+
+sub sent_headers {
+ my ( $self, $value ) = @_;
+
+ if ( defined $value ) {
+ ${*$self}{_sent_headers} = $value;
+ }
+
+ return ${*$self}{_sent_headers};
+}
+
+# Cometd client id
+sub clid {
+ my ( $self, $value ) = @_;
+
+ if ( defined $value ) {
+ ${*$self}{_clid} = $value;
+ }
+
+ return ${*$self}{_clid};
+}
+
+# Cometd transport type
+sub transport {
+ my ( $self, $value ) = @_;
+
+ if ( defined $value ) {
+ ${*$self}{_transport} = $value;
+ }
+
+ return ${*$self}{_transport};
+}
+
+1;
Modified: trunk/server/Slim/Web/JSONRPC.pm
URL: http://svn.slimdevices.com/trunk/server/Slim/Web/JSONRPC.pm?rev=12600&r1=12599&r2=12600&view=diff
==============================================================================
--- trunk/server/Slim/Web/JSONRPC.pm (original)
+++ trunk/server/Slim/Web/JSONRPC.pm Sat Aug 18 14:21:45 2007
@@ -336,7 +336,7 @@
$request->fixEncoding();
# remember we're the source and the $httpClient
- $request->source('JIV');
+ $request->source('JSONRPC');
$request->connectionID($context->{'httpClient'});
if ($context->{'x-jive'}) {
More information about the checkins
mailing list