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