add CGI-2.42, its and testsuite
[p5sagit/p5-mst-13.2.git] / lib / CGI / Push.pm
1 package CGI::Push;
2
3 # See the bottom of this file for the POD documentation.  Search for the
4 # string '=head'.
5
6 # You can run this file through either pod2man or pod2html to produce pretty
7 # documentation in manual or html file format (these utilities are part of the
8 # Perl 5 distribution).
9
10 # Copyright 1995,1996, Lincoln D. Stein.  All rights reserved.
11 # It may be used and modified freely, but I do request that this copyright
12 # notice remain attached to the file.  You may modify this module as you 
13 # wish, but if you redistribute a modified version, please attach a note
14 # listing the modifications you have made.
15
16 # The most recent version and complete docs are available at:
17 #   http://www.genome.wi.mit.edu/ftp/pub/software/WWW/cgi_docs.html
18 #   ftp://ftp-genome.wi.mit.edu/pub/software/WWW/
19
20 $CGI::Push::VERSION='1.01';
21 use CGI;
22 @ISA = ('CGI');
23
24 $CGI::DefaultClass = 'CGI::Push';
25 $CGI::Push::AutoloadClass = 'CGI';
26
27 # add do_push() and push_delay() to exported tags
28 push(@{$CGI::EXPORT_TAGS{':standard'}},'do_push','push_delay');
29
30 sub do_push {
31     my ($self,@p) = CGI::self_or_default(@_);
32
33     # unbuffer output
34     $| = 1;
35     srand;
36     my ($random) = sprintf("%16.0f",rand()*1E16);
37     my ($boundary) = "----------------------------------$random";
38
39     my (@header);
40     my ($type,$callback,$delay,$last_page,$cookie,$target,$expires,@other) =
41         $self->rearrange([TYPE,NEXT_PAGE,DELAY,LAST_PAGE,[COOKIE,COOKIES],TARGET,EXPIRES],@p);
42     $type = 'text/html' unless $type;
43     $callback = \&simple_counter unless $callback && ref($callback) eq 'CODE';
44     $delay = 1 unless defined($delay);
45     $self->push_delay($delay);
46
47     my(@o);
48     foreach (@other) { push(@o,split("=")); }
49     push(@o,'-Target'=>$target) if defined($target);
50     push(@o,'-Cookie'=>$cookie) if defined($cookie);
51     push(@o,'-Type'=>"multipart/x-mixed-replace; boundary=$boundary");
52     push(@o,'-Server'=>"CGI.pm Push Module");
53     push(@o,'-Status'=>'200 OK');
54     push(@o,'-nph'=>1);
55     print $self->header(@o);
56     print "${boundary}$CGI::CRLF";
57     
58     # now we enter a little loop
59     my @contents;
60     while (1) {
61         last unless (@contents = &$callback($self,++$COUNTER)) && defined($contents[0]);
62         print "Content-type: ${type}$CGI::CRLF$CGI::CRLF" 
63             unless $type eq 'dynamic';
64         print @contents,"$CGI::CRLF";
65         print "${boundary}$CGI::CRLF";
66         do_sleep($self->push_delay()) if $self->push_delay();
67     }
68
69     # Optional last page
70     if ($last_page && ref($last_page) eq 'CODE') {
71         print "Content-type: ${type}$CGI::CRLF$CGI::CRLF" unless $type =~ /^dynamic|heterogeneous$/i;
72         print  &$last_page($self,$COUNTER),"$CGI::CRLF${boundary}$CGI::CRLF";
73     }
74 }
75
76 sub simple_counter {
77     my ($self,$count) = @_;
78     return (
79             CGI->start_html("CGI::Push Default Counter"),
80             CGI->h1("CGI::Push Default Counter"),
81             "This page has been updated ",CGI->strong($count)," times.",
82             CGI->hr(),
83             CGI->a({'-href'=>'http://www.genome.wi.mit.edu/ftp/pub/software/WWW/cgi_docs.html'},'CGI.pm home page'),
84             CGI->end_html
85             );
86 }
87
88 sub do_sleep {
89     my $delay = shift;
90     if ( ($delay >= 1) && ($delay!~/\./) ){
91         sleep($delay);
92     } else {
93         select(undef,undef,undef,$delay);
94     }
95 }
96
97 sub push_delay {
98    my ($self,$delay) = CGI::self_or_default(@_);
99    return defined($delay) ? $self->{'.delay'} = 
100         $delay : $self->{'.delay'};
101 }
102
103 1;
104
105 =head1 NAME
106
107 CGI::Push - Simple Interface to Server Push
108
109 =head1 SYNOPSIS
110
111     use CGI::Push qw(:standard);
112
113     do_push(-next_page=>\&next_page,
114             -last_page=>\&last_page,
115             -delay=>0.5);
116
117     sub next_page {
118         my($q,$counter) = @_;
119         return undef if $counter >= 10;
120         return start_html('Test'),
121                h1('Visible'),"\n",
122                "This page has been called ", strong($counter)," times",
123                end_html();
124       }
125
126      sub last_page {
127          my($q,$counter) = @_;
128          return start_html('Done'),
129                 h1('Finished'),
130                 strong($counter),' iterations.',
131                 end_html;
132      }
133
134 =head1 DESCRIPTION
135
136 CGI::Push is a subclass of the CGI object created by CGI.pm.  It is
137 specialized for server push operations, which allow you to create
138 animated pages whose content changes at regular intervals.
139
140 You provide CGI::Push with a pointer to a subroutine that will draw
141 one page.  Every time your subroutine is called, it generates a new
142 page.  The contents of the page will be transmitted to the browser
143 in such a way that it will replace what was there beforehand.  The
144 technique will work with HTML pages as well as with graphics files, 
145 allowing you to create animated GIFs.
146
147 =head1 USING CGI::Push
148
149 CGI::Push adds one new method to the standard CGI suite, do_push().
150 When you call this method, you pass it a reference to a subroutine
151 that is responsible for drawing each new page, an interval delay, and
152 an optional subroutine for drawing the last page.  Other optional
153 parameters include most of those recognized by the CGI header()
154 method.
155
156 You may call do_push() in the object oriented manner or not, as you
157 prefer:
158
159     use CGI::Push;
160     $q = new CGI::Push;
161     $q->do_push(-next_page=>\&draw_a_page);
162
163         -or-
164
165     use CGI::Push qw(:standard);
166     do_push(-next_page=>\&draw_a_page);
167
168 Parameters are as follows:
169
170 =over 4
171
172 =item -next_page
173
174     do_push(-next_page=>\&my_draw_routine);
175
176 This required parameter points to a reference to a subroutine responsible for
177 drawing each new page.  The subroutine should expect two parameters
178 consisting of the CGI object and a counter indicating the number
179 of times the subroutine has been called.  It should return the
180 contents of the page as an B<array> of one or more items to print.  
181 It can return a false value (or an empty array) in order to abort the
182 redrawing loop and print out the final page (if any)
183
184     sub my_draw_routine {
185         my($q,$counter) = @_;
186         return undef if $counter > 100;
187         return start_html('testing'),
188                h1('testing'),
189                "This page called $counter times";
190     }
191
192 You are of course free to refer to create and use global variables
193 within your draw routine in order to achieve special effects.
194
195 =item -last_page
196
197 This optional parameter points to a reference to the subroutine
198 responsible for drawing the last page of the series.  It is called
199 after the -next_page routine returns a false value.  The subroutine
200 itself should have exactly the same calling conventions as the
201 -next_page routine.
202
203 =item -type
204
205 This optional parameter indicates the content type of each page.  It
206 defaults to "text/html".  Normally the module assumes that each page
207 is of a homogenous MIME type.  However if you provide either of the
208 magic values "heterogeneous" or "dynamic" (the latter provided for the
209 convenience of those who hate long parameter names), you can specify
210 the MIME type -- and other header fields -- on a per-page basis.  See 
211 "heterogeneous pages" for more details.
212
213 =item -delay
214
215 This indicates the delay, in seconds, between frames.  Smaller delays
216 refresh the page faster.  Fractional values are allowed.
217
218 B<If not specified, -delay will default to 1 second>
219
220 =item -cookie, -target, -expires
221
222 These have the same meaning as the like-named parameters in
223 CGI::header().
224
225 =back
226
227 =head2 Heterogeneous Pages
228
229 Ordinarily all pages displayed by CGI::Push share a common MIME type.
230 However by providing a value of "heterogeneous" or "dynamic" in the
231 do_push() -type parameter, you can specify the MIME type of each page
232 on a case-by-case basis.  
233
234 If you use this option, you will be responsible for producing the
235 HTTP header for each page.  Simply modify your draw routine to
236 look like this:
237
238     sub my_draw_routine {
239         my($q,$counter) = @_;
240         return header('text/html'),   # note we're producing the header here
241                start_html('testing'),
242                h1('testing'),
243                "This page called $counter times";
244     }
245
246 You can add any header fields that you like, but some (cookies and
247 status fields included) may not be interpreted by the browser.  One
248 interesting effect is to display a series of pages, then, after the
249 last page, to redirect the browser to a new URL.  Because redirect() 
250 does b<not> work, the easiest way is with a -refresh header field,
251 as shown below:
252
253     sub my_draw_routine {
254         my($q,$counter) = @_;
255         return undef if $counter > 10;
256         return header('text/html'),   # note we're producing the header here
257                start_html('testing'),
258                h1('testing'),
259                "This page called $counter times";
260     }
261    
262     sub my_last_page {
263         header(-refresh=>'5; URL=http://somewhere.else/finished.html',
264                -type=>'text/html'),
265         start_html('Moved'),
266         h1('This is the last page'),
267         'Goodbye!'
268          hr,    
269          end_html; 
270     }
271
272 =head2 Changing the Page Delay on the Fly
273
274 If you would like to control the delay between pages on a page-by-page
275 basis, call push_delay() from within your draw routine.  push_delay()
276 takes a single numeric argument representing the number of seconds you
277 wish to delay after the current page is displayed and before
278 displaying the next one.  The delay may be fractional.  Without
279 parameters, push_delay() just returns the current delay.
280
281 =head1 INSTALLING CGI::Push SCRIPTS
282
283 Server push scripts B<must> be installed as no-parsed-header (NPH)
284 scripts in order to work correctly.  On Unix systems, this is most
285 often accomplished by prefixing the script's name with "nph-".  
286 Recognition of NPH scripts happens automatically with WebSTAR and 
287 Microsoft IIS.  Users of other servers should see their documentation
288 for help.
289
290 =head1 CAVEATS
291
292 This is a new module.  It hasn't been extensively tested.
293
294 =head1 AUTHOR INFORMATION
295
296 be used and modified freely, but I do request that this copyright
297 notice remain attached to the file.  You may modify this module as you
298 wish, but if you redistribute a modified version, please attach a note
299 listing the modifications you have made.
300
301 Address bug reports and comments to:
302 lstein@genome.wi.mit.edu
303
304 =head1 BUGS
305
306 This section intentionally left blank.
307
308 =head1 SEE ALSO
309
310 L<CGI::Carp>, L<CGI>
311
312 =cut
313