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 |
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 |
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 |
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 |
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 |
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 |
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 |
105 | my ($self,$delay) = CGI::self_or_default(@_); |
106 | return defined($delay) ? $self->{'.delay'} = |
107 | $delay : $self->{'.delay'}; |
424ec8fa |
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 |
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 |
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 |
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 |
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 |
235 | If not specified, -nph will default to 1 (as needed for many servers, see below). |
236 | |
54310121 |
237 | =back |
238 | |
424ec8fa |
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 |
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 |
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 |
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 |
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 |
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 |
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 | |