/[meteor]/googlecode.com/svn/trunk/Meteor/Channel.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /googlecode.com/svn/trunk/Meteor/Channel.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 53 - (hide annotations)
Wed Feb 27 21:58:56 2008 UTC (16 years, 1 month ago) by andrew.betts
File size: 8017 byte(s)
Updated version number

JS client:
Added channel info handler to JS client, assumes Meteor will send <script>ch('channel', msgid);</script>
Allowed processing of messages prior to current message index
Added disconnect() to eof()
Revert to poll mode if unable to load frame (should fix IE proxy issues)

Server:
Fixed output of channel info to show only subscribed channels (and simplified)
Added logging of IP addresses

1 knops.gerd 11 #!/usr/bin/perl -w
2     ###############################################################################
3     # Meteor
4     # An HTTP server for the 2.0 web
5     # Copyright (c) 2006 contributing authors
6     #
7     # Subscriber.pm
8     #
9     # Description:
10     # A Meteor Channel
11     #
12     ###############################################################################
13     #
14     # This program is free software; you can redistribute it and/or modify it
15     # under the terms of the GNU General Public License as published by the Free
16     # Software Foundation; either version 2 of the License, or (at your option)
17     # any later version.
18     #
19     # This program is distributed in the hope that it will be useful, but WITHOUT
20     # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21     # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
22     # more details.
23     #
24     # You should have received a copy of the GNU General Public License along
25     # with this program; if not, write to the Free Software Foundation, Inc.,
26     # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27     #
28     # For more information visit www.meteorserver.org
29     #
30     ###############################################################################
31    
32     package Meteor::Channel;
33     ###############################################################################
34     # Configuration
35     ###############################################################################
36    
37     use strict;
38    
39     use Meteor::Message;
40    
41     our %Channels=();
42     our $MessageID=0;
43    
44     ###############################################################################
45     # Class methods
46     ###############################################################################
47     sub channelWithName {
48     my $class=shift;
49     my $channelName=shift;
50     my $avoidCreation=shift;
51    
52     unless(exists($Channels{$channelName}))
53     {
54     return undef if($avoidCreation);
55     #
56     # Create new channel
57     #
58     $Channels{$channelName}=$class->newChannel($channelName);
59    
60     &::syslog('debug',"New channel $channelName");
61     }
62    
63     return $Channels{$channelName};
64     }
65    
66     sub listChannels {
67     my $class=shift;
68    
69     my $list='';
70     foreach my $channelName (sort keys %Channels)
71     {
72     my $channel=$Channels{$channelName};
73    
74     $list.=$channelName.'('.$channel->messageCount().'/'.$channel->subscriberCount().")$::CRLF";
75     }
76    
77     $list;
78     }
79    
80     sub deleteChannel {
81     my $class=shift;
82     my $channelName=shift;
83    
84     delete($Channels{$channelName});
85     }
86    
87     sub trimMessageStoresByTimestamp {
88     my $class=shift;
89     my $minTimeStamp=shift;
90    
91     return unless($minTimeStamp);
92    
93     map { $_->trimMessageStoreByTimestamp($minTimeStamp) } (values %Channels);
94     }
95    
96     sub clearAllBuffers {
97     my $class=shift;
98    
99     map { $_->clearBuffer() } (values %Channels);
100     }
101    
102 knops.gerd 25 sub numChannels {
103    
104     return scalar(keys %Channels);
105     }
106    
107 knops.gerd 11 ###############################################################################
108     # Factory methods
109     ###############################################################################
110     sub new {
111     #
112     # Create a new empty instance
113     #
114     my $class=shift;
115    
116     my $obj={};
117    
118     bless($obj,$class);
119     }
120    
121     sub newChannel {
122     #
123     # new instance from new server connection
124     #
125     my $self=shift->new();
126    
127     my $name=shift;
128     $self->{'name'}=$name;
129    
130     $self->{'subscribers'}=[];
131     $self->{'messages'}=[];
132    
133     $self;
134     }
135    
136     sub DESTROY {
137     my $self=shift;
138    
139     my @subscribers=@{$self->{'subscribers'}};
140 knops.gerd 13 map { $_->closeChannel($self->{'name'}) } @subscribers;
141 knops.gerd 11 }
142    
143     ###############################################################################
144     # Instance methods
145     ###############################################################################
146     sub name {
147     shift->{'name'};
148     }
149    
150     sub addSubscriber {
151     my $self=shift;
152     my $subscriber=shift;
153     my $startId=shift;
154     my $persist=shift;
155 knops.gerd 47 my $mode=shift || '';
156     my $userAgent=shift || '';
157 knops.gerd 11
158     # Note: negative $startId means go back that many messages
159 andrew.betts 50 my $startIndex=$self->indexForMessageID($startId);
160     my $logStartIndex = $startIndex || $self->lastMsgID() || 0;
161 knops.gerd 11
162     push(@{$self->{'subscribers'}},$subscriber) if($persist);
163    
164 knops.gerd 47 &::syslog('info','',
165     'joinchannel',
166 andrew.betts 53 $subscriber->{'ip'},
167 knops.gerd 47 $subscriber->{'subscriberID'},
168     $self->{'name'},
169     $mode,
170 andrew.betts 50 $logStartIndex,
171 knops.gerd 47 $userAgent
172     );
173    
174 knops.gerd 11 return unless(defined($startIndex));
175    
176     my $msgCount=scalar(@{$self->{'messages'}});
177     my $txt='';
178    
179     $startIndex=0 if($startIndex<0);
180    
181 andrew.betts 50 if($startIndex<$msgCount) {
182     $subscriber->sendMessages(@{$self->{'messages'}}[$startIndex..$msgCount-1]);
183 knops.gerd 11 }
184     }
185    
186     sub removeSubscriber {
187     my $self=shift;
188     my $subscriber=shift;
189 knops.gerd 47 my $reason=shift ||'unknown';
190 knops.gerd 11
191     my $idx=undef;
192 andrew.betts 50 my $numsubs = scalar(@{$self->{'subscribers'}});
193 andrew.betts 53
194     for (my $i=0; $i<$numsubs; $i++) {
195     if($self->{'subscribers'}->[$i]==$subscriber) {
196 knops.gerd 11 $idx=$i;
197     last;
198     }
199     }
200    
201     if(defined($idx))
202     {
203     splice(@{$self->{'subscribers'}},$idx,1);
204 knops.gerd 47
205 andrew.betts 50 my $timeConnected = time - $subscriber->{'ConnectionStart'};
206 knops.gerd 47 &::syslog('info','',
207     'leavechannel',
208 andrew.betts 53 $subscriber->{'ip'},
209 knops.gerd 47 $subscriber->{'subscriberID'},
210     $self->{'name'},
211 andrew.betts 50 $timeConnected,
212 knops.gerd 47 $subscriber->{'MessageCount'},
213     $subscriber->{'bytesWritten'},
214     $reason
215     );
216 knops.gerd 11 }
217    
218     $self->checkExpiration();
219     }
220    
221     sub subscriberCount {
222     my $self=shift;
223    
224     scalar(@{$self->{'subscribers'}});
225     }
226    
227     sub addMessage {
228     my $self=shift;
229     my $messageText=shift;
230    
231     my $message=Meteor::Message->newWithID($MessageID++);
232 knops.gerd 16 $message->setText($messageText);
233     $message->setChannelName($self->{'name'});
234 knops.gerd 11 push(@{$self->{'messages'}},$message);
235 andrew.betts 50 &::syslog('debug',"New message ".$message->{"id"}." on channel ".$self->{'name'});
236 knops.gerd 11
237     $self->trimMessageStoreBySize();
238    
239 knops.gerd 45 map { $_->sendMessages($message) } @{$self->{'subscribers'}};
240 knops.gerd 46
241     $message;
242 knops.gerd 11 }
243    
244     sub messageCount {
245     my $self=shift;
246    
247     scalar(@{$self->{'messages'}});
248     }
249    
250     sub trimMessageStoreBySize {
251     my $self=shift;
252    
253     my $numMessages=scalar(@{$self->{'messages'}});
254    
255     if($numMessages>$::CONF{'MaxMessagesPerChannel'})
256     {
257     splice(@{$self->{'messages'}},0,-$::CONF{'MaxMessagesPerChannel'});
258     }
259     }
260    
261     sub trimMessageStoreByTimestamp {
262     my $self=shift;
263     my $ts=shift;
264    
265     while(scalar(@{$self->{'messages'}})>0 && $self->{'messages'}->[0]->timestamp()<$ts)
266     {
267     my $msg=shift(@{$self->{'messages'}});
268     }
269    
270     $self->checkExpiration();
271     }
272    
273     sub clearBuffer {
274     my $self=shift;
275    
276     $self->{'messages'}=[];
277    
278     $self->checkExpiration();
279     }
280    
281     sub checkExpiration {
282     my $self=shift;
283    
284     if($self->messageCount()==0 && $self->subscriberCount()==0)
285     {
286     my $name=$self->name();
287     &::syslog('debug',"Channel expired: $name");
288     $self->deleteChannel($name);
289     }
290     }
291    
292     sub indexForMessageID {
293     my $self=shift;
294     my $id=shift;
295    
296     # the messages is always sorted by ID, so we can
297     # use a binary search to find the message.
298     # return undef if there are no messages or the
299     # ID is that of the last message.
300     # Otherwise return the ID of the found message
301     # of if no message with that ID exists the one
302     # with the next higher ID
303     #
304     return undef unless(defined($id));
305    
306 knops.gerd 46 my $numMessages=scalar(@{$self->{'messages'}});
307 knops.gerd 11
308     return undef unless($numMessages);
309     return -1 unless($id ne '');
310    
311     # Note: negative $id means go back that many messages
312     return $numMessages+$id if($id<0);
313    
314     my $low=0;
315     my $high=$numMessages-1;
316     my $mid;
317     my $cond;
318     while($low<=$high)
319     {
320     $mid=($low+$high)>>1;
321     $cond=$id <=> $self->{'messages'}->[$mid]->id();
322     if($cond<0)
323     {
324     $high=$mid-1;
325     }
326     elsif($cond>0)
327     {
328     $low=$mid+1;
329     }
330     else
331     {
332     return $mid;
333     }
334     }
335    
336     return undef if($low>=$numMessages);
337    
338     return $low;
339     }
340    
341 knops.gerd 46 sub lastMsgID {
342     my $self=shift;
343     my $numMessages=scalar(@{$self->{'messages'}});
344 andrew.betts 50 return undef unless($numMessages>0);
345 knops.gerd 46 @{$self->{'messages'}}[-1]->id();
346     }
347    
348 knops.gerd 45 sub descriptionWithTemplate {
349     my $self=shift;
350     my $template=shift;
351    
352 andrew.betts 53 return '' unless(defined($template) && $template ne '');
353    
354 knops.gerd 45 $template=~s/~([a-zA-Z0-9_]*)~/
355 andrew.betts 50 if(!defined($1) || $1 eq '') {
356 knops.gerd 45 '~';
357 andrew.betts 50 } elsif($1 eq 'messageCount') {
358 knops.gerd 45 $self->messageCount();
359 andrew.betts 50 } elsif($1 eq 'subscriberCount') {
360 knops.gerd 45 $self->subscriberCount();
361 andrew.betts 50 } elsif($1 eq 'lastMsgID') {
362 andrew.betts 53 $self->lastMsgID() || 0;
363 andrew.betts 50 } elsif($1 eq 'name') {
364     $self->{'name'};
365     } else {
366 knops.gerd 45 '';
367     }
368     /gex;
369    
370     $template;
371     }
372    
373 knops.gerd 11 1;
374 andrew.betts 3 ############################################################################EOF

  ViewVC Help
Powered by ViewVC 1.1.26