/[meteor]/googlecode.com/svn/trunk/Meteor/Subscriber.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

Diff of /googlecode.com/svn/trunk/Meteor/Subscriber.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 9 by andrew.betts, Fri Dec 8 16:52:58 2006 UTC revision 11 by knops.gerd, Thu Dec 14 16:29:42 2006 UTC
# Line 1  Line 1 
1  #!/usr/bin/perl -w  #!/usr/bin/perl -w
2  ###############################################################################  ###############################################################################
3  #   Meteor  #   Meteor
4  #   An HTTP server for the 2.0 web  #   An HTTP server for the 2.0 web
5  #   Copyright (c) 2006 contributing authors  #   Copyright (c) 2006 contributing authors
6  #  #
7  #   Subscriber.pm  #   Subscriber.pm
8  #  #
9  #       Description:  #       Description:
10  #       A Meteor Subscriber  #       A Meteor Subscriber
11  #  #
12  ###############################################################################  ###############################################################################
13  #  #
14  #   This program is free software; you can redistribute it and/or modify it  #   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  #   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)  #   Software Foundation; either version 2 of the License, or (at your option)
17  #   any later version.  #   any later version.
18  #  #
19  #   This program is distributed in the hope that it will be useful, but WITHOUT  #   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  #   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  #   FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for  #   FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
22  #   more details.  #   more details.
23  #  #
24  #   You should have received a copy of the GNU General Public License along  #   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.,  #   with this program; if not, write to the Free Software Foundation, Inc.,
26  #   59 Temple Place, Suite 330, Boston, MA 02111-1307 USA  #   59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27  #  #
28  #   For more information visit www.meteorserver.org  #   For more information visit www.meteorserver.org
29  #  #
30  ###############################################################################  ###############################################################################
31    
32  package Meteor::Subscriber;  package Meteor::Subscriber;
33  ###############################################################################  ###############################################################################
34  # Configuration  # Configuration
35  ###############################################################################  ###############################################################################
36                    
37          use strict;          use strict;
38                    
39          use Meteor::Connection;          use Meteor::Connection;
40          use Meteor::Channel;          use Meteor::Channel;
41          use Meteor::Document;          use Meteor::Document;
42                    
43          @Meteor::Subscriber::ISA=qw(Meteor::Connection);          @Meteor::Subscriber::ISA=qw(Meteor::Connection);
44                    
45          our %PersistentConnections=();          our %PersistentConnections=();
46    
47  ###############################################################################  ###############################################################################
48  # Factory methods  # Factory methods
49  ###############################################################################  ###############################################################################
50  sub newFromServer {  sub newFromServer {
51          my $class=shift;          my $class=shift;
52                    
53          my $self=$class->SUPER::newFromServer(shift);          my $self=$class->SUPER::newFromServer(shift);
54                    
55          $self->{'headerBuffer'}='';          $self->{'headerBuffer'}='';
56          $self->{'MessageCount'}=0;          $self->{'MessageCount'}=0;
57          $self->{'MaxMessageCount'}=0;          $self->{'MaxMessageCount'}=0;
58                    
59          $self->{'ConnectionStart'}=time;          $self->{'ConnectionStart'}=time;
60          my $maxTime=$::CONF{'MaxTime'};          my $maxTime=$::CONF{'MaxTime'};
61          if($maxTime>0)          if($maxTime>0)
62          {          {
63                  $self->{'ConnectionTimeLimit'}=$self->{'ConnectionStart'}+$maxTime;                  $self->{'ConnectionTimeLimit'}=$self->{'ConnectionStart'}+$maxTime;
64          }          }
65                    
66          $self;          $self;
67  }  }
68    
69  ###############################################################################  ###############################################################################
70  # Class methods  # Class methods
71  ###############################################################################  ###############################################################################
72  sub deleteSubscriberWithID {  sub deleteSubscriberWithID {
73          my $class=shift;          my $class=shift;
74          my $id=shift;          my $id=shift;
75                    
76          if(exists($PersistentConnections{$id}))          if(exists($PersistentConnections{$id}))
77          {          {
78                  $PersistentConnections{$id}->close(1);                  $PersistentConnections{$id}->close(1);
79          }          }
80  }  }
81    
82  sub pingPersistentConnections {  sub pingPersistentConnections {
83          my $class=shift;          my $class=shift;
84                    
85          my $msg=$::CONF{'PingMessage'};          my $msg=$::CONF{'PingMessage'};
86          my @cons=values %PersistentConnections;          my @cons=values %PersistentConnections;
87                    
88          map { $_->write($msg) } @cons;          map { $_->write($msg) } @cons;
89  }  }
90    
91  sub checkPersistentConnectionsForMaxTime {  sub checkPersistentConnectionsForMaxTime {
92          my $class=shift;          my $class=shift;
93                    
94          my $time=time;          my $time=time;
95          my @cons=values %PersistentConnections;          my @cons=values %PersistentConnections;
96                    
97          map { $_->checkForMaxTime($time) } @cons;          map { $_->checkForMaxTime($time) } @cons;
98  }  }
99    
100  ###############################################################################  ###############################################################################
101  # Instance methods  # Instance methods
102  ###############################################################################  ###############################################################################
103  sub processLine {  sub processLine {
104          my $self=shift;          my $self=shift;
105          my $line=shift;          my $line=shift;
106                    
107          # Once the header was processed we ignore any input          # Once the header was processed we ignore any input
108          return unless(exists($self->{'headerBuffer'}));          return unless(exists($self->{'headerBuffer'}));
109                    
110          if($line ne '')          if($line ne '')
111          {          {
112                  #                  #
113                  # Accumulate header                  # Accumulate header
114                  #                  #
115                  $self->{'headerBuffer'}.="$line\n";                  $self->{'headerBuffer'}.="$line\n";
116          }          }
117          else          else
118          {          {
119                  #                  #
120                  # Empty line signals end of header.                  # Empty line signals end of header.
121                  # Analyze header, register with appropiate channel                  # Analyze header, register with appropiate channel
122                  # and send pending messages.                  # and send pending messages.
123                  #                  #
124                  # GET $::CONF{'SubscriberDynamicPageAddress'}?channel=ml123&restartfrom=1 HTTP/1.1                  # GET $::CONF{'SubscriberDynamicPageAddress'}?channel=ml123&restartfrom=1 HTTP/1.1
125                  #                  #
126                  # Find the 'GET' line                  # Find the 'GET' line
127                  #                  #
128                  if($self->{'headerBuffer'}=~/GET\s+$::CONF{'SubscriberDynamicPageAddress'}\?(\S+)/)                  if($self->{'headerBuffer'}=~/GET\s+$::CONF{'SubscriberDynamicPageAddress'}\?(\S+)/)
129                  {                  {
130                          my @formData=split('&',$1);                          my @formData=split('&',$1);
131                          my $channelName=undef;                          my $channelName=undef;
132                          my $startIndex=undef;                          my $startIndex=undef;
133                          my $backtrack=undef;                          my $backtrack=undef;
134                          my $persist=1;                          my $persist=1;
135                          my $subscriberID=undef;                          my $subscriberID=undef;
136                          foreach my $formElement (@formData)                          foreach my $formElement (@formData)
137                          {                          {
138                                  if($formElement=~/^channel=(.+)$/)                                  if($formElement=~/^channel=(.+)$/)
139                                  {                                  {
140                                          $channelName=$1;                                          $channelName=$1;
141                                  }                                  }
142                                  elsif($formElement=~/^restartfrom=(\d*)$/)                                  elsif($formElement=~/^restartfrom=(\d*)$/)
143                                  {                                  {
144                                          $startIndex=$1;                                          $startIndex=$1;
145                                          $startIndex='' unless(defined($startIndex));                                          $startIndex='' unless(defined($startIndex));
146                                  }                                  }
147                                  elsif($formElement=~/^backtrack=(\d+)$/)                                  elsif($formElement=~/^backtrack=(\d+)$/)
148                                  {                                  {
149                                          $backtrack=$1;                                          $backtrack=$1;
150                                          $backtrack=0 unless(defined($backtrack));                                          $backtrack=0 unless(defined($backtrack));
151                                  }                                  }
152                                  elsif($formElement=~/^persist=(?i)(yes|true|1|no|false|0)$/)                                  elsif($formElement=~/^persist=(?i)(yes|true|1|no|false|0)$/)
153                                  {                                  {
154                                          $persist=0 if($1=~/(no|false|0)/i);                                          $persist=0 if($1=~/(no|false|0)/i);
155                                  }                                  }
156                                  elsif($formElement=~/^id=(.+)$/)                                  elsif($formElement=~/^id=(.+)$/)
157                                  {                                  {
158                                          $subscriberID=$1;                                          $subscriberID=$1;
159                                  }                                  }
160                                  elsif($formElement=~/^maxmessages=(\d+)$/i)                                  elsif($formElement=~/^maxmessages=(\d+)$/i)
161                                  {                                  {
162                                          $self->{'MaxMessageCount'}=$1;                                          $self->{'MaxMessageCount'}=$1;
163                                  }                                  }
164                                  elsif($formElement=~/^template=(\d+)$/i)                                  elsif($formElement=~/^template=(\d+)$/i)
165                                  {                                  {
166                                          $self->{'HeaderTemplateNumber'}=$1;                                          $self->{'HeaderTemplateNumber'}=$1;
167                                  }                                  }
168                                  elsif($formElement=~/^maxtime=(\d+)$/i)                                  elsif($formElement=~/^maxtime=(\d+)$/i)
169                                  {                                  {
170                                          my $clientRequest=$1;                                          my $clientRequest=$1;
171                                          my $serverDefault=$::CONF{'MaxTime'};                                          my $serverDefault=$::CONF{'MaxTime'};
172                                                                                    
173                                          if($serverDefault==0 || $serverDefault>$clientRequest)                                          if($serverDefault==0 || $serverDefault>$clientRequest)
174                                          {                                          {
175                                                  $self->{'ConnectionTimeLimit'}=$self->{'ConnectionStart'}+$clientRequest;                                                  $self->{'ConnectionTimeLimit'}=$self->{'ConnectionStart'}+$clientRequest;
176                                          }                                          }
177                                  }                                  }
178                          }                          }
179                                                                                                    
180                          delete($self->{'headerBuffer'});                          delete($self->{'headerBuffer'});
181                                                    
182                          if(defined($startIndex) && defined($backtrack))                          if(defined($startIndex) && defined($backtrack))
183                          {                          {
184                                  $self->emitHeader("404 Cannot use both 'restartfrom' and 'backtrack'");                                  $self->emitHeader("404 Cannot use both 'restartfrom' and 'backtrack'");
185                                  $self->close();                                  $self->close();
186                                                                    
187                                  return;                                  return;
188                          }                          }
189                                                    
190                          if(defined($subscriberID) && $persist)                          if(defined($subscriberID) && $persist)
191                          {                          {
192                                  $self->{'subscriberID'}=$subscriberID;                                  $self->{'subscriberID'}=$subscriberID;
193                                  $self->deleteSubscriberWithID($subscriberID);                                  $self->deleteSubscriberWithID($subscriberID);
194                                  $PersistentConnections{$subscriberID}=$self;                                  $PersistentConnections{$subscriberID}=$self;
195                          }                          }
196                                                    
197                          if(defined($channelName))                          if(defined($channelName))
198                          {                          {
199                                  $self->emitOKHeader();                                  $self->emitOKHeader();
200                                                                    
201                                  $startIndex=-$backtrack if(!defined($startIndex) && defined($backtrack));                                  $startIndex=-$backtrack if(!defined($startIndex) && defined($backtrack));
202                                                                    
203                                  $self->setChannelName($channelName,$startIndex,$persist);                                  $self->setChannelName($channelName,$startIndex,$persist);
204                                                                    
205                                  $self->close(1) unless($persist);                                  $self->close(1) unless($persist);
206                                                                    
207                                  return;                                  return;
208                          }                          }
209                  }                  }
210                  elsif($self->{'headerBuffer'}=~/GET\s+([^\s\?]+)/)                  elsif($self->{'headerBuffer'}=~/GET\s+([^\s\?]+)/)
211                  {                  {
212                          Meteor::Document->serveFileToClient($1,$self);                          Meteor::Document->serveFileToClient($1,$self);
213                                                    
214                          $self->close(1);                          $self->close(1);
215                                                    
216                          return;                          return;
217                  }                  }
218                                    
219                  #                  #
220                  # If we fall through we did not understand the request                  # If we fall through we did not understand the request
221                  #                  #
222                  $self->emitErrorHeader();                  $self->emitErrorHeader();
223          }          }
224  }  }
225    
226  sub setChannelName {  sub setChannelName {
227          my $self=shift;          my $self=shift;
228          my $channelName=shift;          my $channelName=shift;
229          my $startIndex=shift;          my $startIndex=shift;
230          my $persist=shift;          my $persist=shift;
231                    
232          my $channel=Meteor::Channel->channelWithName($channelName);          my $channel=Meteor::Channel->channelWithName($channelName);
233          $self->{'channel'}=$channel if($persist);          $self->{'channel'}=$channel if($persist);
234                    
235          $channel->addSubscriber($self,$startIndex,$persist);          $channel->addSubscriber($self,$startIndex,$persist);
236  }  }
237    
238  sub emitOKHeader {  sub emitOKHeader {
239          my $self=shift;          my $self=shift;
240                    
241          $self->emitHeader('200 OK');          $self->emitHeader('200 OK');
242  }  }
243    
244  sub emitErrorHeader {  sub emitErrorHeader {
245          my $self=shift;          my $self=shift;
246                    
247          $self->emitHeader('404 Not Found');          $self->emitHeader('404 Not Found');
248                    
249          # close up shop here!          # close up shop here!
250          $self->close();          $self->close();
251  }  }
252    
253  sub emitHeader {  sub emitHeader {
254          my $self=shift;          my $self=shift;
255          my $status=shift;          my $status=shift;
256                    
257          my $header=undef;          my $header=undef;
258          if(exists($self->{'HeaderTemplateNumber'}))          if(exists($self->{'HeaderTemplateNumber'}))
259          {          {
260                  my $hn='HeaderTemplate'.$self->{'HeaderTemplateNumber'};                  my $hn='HeaderTemplate'.$self->{'HeaderTemplateNumber'};
261                                    
262                  $header=$::CONF{$hn};                  $header=$::CONF{$hn};
263          }          }
264          $header=$::CONF{'HeaderTemplate'} unless(defined($header));          $header=$::CONF{'HeaderTemplate'} unless(defined($header));
265                    
266          $header=~s/~([^~]+)~/          $header=~s/~([^~]+)~/
267                  if(!defined($1) || $1 eq '')                  if(!defined($1) || $1 eq '')
268                  {                  {
269                          '~';                          '~';
270                  }                  }
271                  elsif($1 eq 'server')                  elsif($1 eq 'server')
272                  {                  {
273                          $::PGM;                          $::PGM;
274                  }                  }
275                  elsif($1 eq 'status')                  elsif($1 eq 'status')
276                  {                  {
277                          $status;                          $status;
278                  }                  }
279                  elsif($1 eq 'servertime')                  elsif($1 eq 'servertime')
280                  {                  {
281                          time;                          time;
282                  }                  }
283                  else                  else
284                  {                  {
285                          '';                          '';
286                  }                  }
287          /gex;          /gex;
288                    
289          $self->write($header);          $self->write($header);
290  }  }
291    
292  sub sendMessage {  sub sendMessage {
293          my $self=shift;          my $self=shift;
294          my $msg=shift;          my $msg=shift;
295                    
296          $self->write($msg);          $self->write($msg);
297                    
298          my $msgCount=++$self->{'MessageCount'};          my $msgCount=++$self->{'MessageCount'};
299                    
300          my $maxMsg=$::CONF{'MaxMessages'};          my $maxMsg=$::CONF{'MaxMessages'};
301          if(defined($maxMsg) && $maxMsg>0 && $msgCount>=$maxMsg)          if(defined($maxMsg) && $maxMsg>0 && $msgCount>=$maxMsg)
302          {          {
303                  $self->close(1);                  $self->close(1);
304          }          }
305                    
306          if($self->{'MaxMessageCount'}>0 && $msgCount>=$self->{'MaxMessageCount'})          if($self->{'MaxMessageCount'}>0 && $msgCount>=$self->{'MaxMessageCount'})
307          {          {
308                  $self->close(1);                  $self->close(1);
309          }          }
310  }  }
311    
312  sub close {  sub close {
313          my $self=shift;          my $self=shift;
314          my $noShutdownMsg=shift;          my $noShutdownMsg=shift;
315                    
316          $self->{'channel'}->removeSubscriber($self) if($self->{'channel'});          $self->{'channel'}->removeSubscriber($self) if($self->{'channel'});
317          delete($self->{'channel'});          delete($self->{'channel'});
318                    
319          if(exists($self->{'subscriberID'}))          if(exists($self->{'subscriberID'}))
320          {          {
321                  delete($PersistentConnections{$self->{'subscriberID'}});                  delete($PersistentConnections{$self->{'subscriberID'}});
322          }          }
323                    
324          #          #
325          # Send shutdown message unless remote closed or          # Send shutdown message unless remote closed or
326          # connection not yet established          # connection not yet established
327          #          #
328          unless($noShutdownMsg || $self->{'remoteClosed'} || exists($self->{'headerBuffer'}))          unless($noShutdownMsg || $self->{'remoteClosed'} || exists($self->{'headerBuffer'}))
329          {          {
330                  my $msg=$::CONF{'SubscriberShutdownMsg'};                  my $msg=$::CONF{'SubscriberShutdownMsg'};
331                  if(defined($msg) && $msg ne '')                  if(defined($msg) && $msg ne '')
332                  {                  {
333                          $self->write($msg);                          $self->write($msg);
334                  }                  }
335          }          }
336                    
337          $self->SUPER::close();          $self->SUPER::close();
338  }  }
339    
340  sub checkForMaxTime {  sub checkForMaxTime {
341          my $self=shift;          my $self=shift;
342          my $time=shift;          my $time=shift;
343                    
344          $self->close(1) if(exists($self->{'ConnectionTimeLimit'}) && $self->{'ConnectionTimeLimit'}<$time);          $self->close(1) if(exists($self->{'ConnectionTimeLimit'}) && $self->{'ConnectionTimeLimit'}<$time);
345  }  }
346    
347  1;  1;
348  ############################################################################EOF  ############################################################################EOF

Legend:
Removed from v.9  
changed lines
  Added in v.11

  ViewVC Help
Powered by ViewVC 1.1.26