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-2000, 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://stein.cshl.org/WWW/software/CGI/
|
---|
18 |
|
---|
19 | $CGI::Push::VERSION='1.04';
|
---|
20 | use CGI;
|
---|
21 | use CGI::Util 'rearrange';
|
---|
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("%08.0f",rand()*1E8);
|
---|
37 | my ($boundary) = "----=_NeXtPaRt$random";
|
---|
38 |
|
---|
39 | my (@header);
|
---|
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);
|
---|
41 | $type = 'text/html' unless $type;
|
---|
42 | $callback = \&simple_counter unless $callback && ref($callback) eq 'CODE';
|
---|
43 | $delay = 1 unless defined($delay);
|
---|
44 | $self->push_delay($delay);
|
---|
45 | $nph = 1 unless defined($nph);
|
---|
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") if $nph;
|
---|
53 | push(@o,'-Status'=>'200 OK');
|
---|
54 | push(@o,'-nph'=>1) if $nph;
|
---|
55 | print $self->header(@o);
|
---|
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);
|
---|
62 |
|
---|
63 | # now we enter a little loop
|
---|
64 | while (1) {
|
---|
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 | }
|
---|
81 | }
|
---|
82 | print "WARNING: YOUR BROWSER DOESN'T SUPPORT THIS SERVER-PUSH TECHNOLOGY.$CGI::CRLF";
|
---|
83 | }
|
---|
84 |
|
---|
85 | sub simple_counter {
|
---|
86 | my ($self,$count) = @_;
|
---|
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;
|
---|
93 | }
|
---|
94 |
|
---|
95 | sub do_sleep {
|
---|
96 | my $delay = shift;
|
---|
97 | if ( ($delay >= 1) && ($delay!~/\./) ){
|
---|
98 | sleep($delay);
|
---|
99 | } else {
|
---|
100 | select(undef,undef,undef,$delay);
|
---|
101 | }
|
---|
102 | }
|
---|
103 |
|
---|
104 | sub push_delay {
|
---|
105 | my ($self,$delay) = CGI::self_or_default(@_);
|
---|
106 | return defined($delay) ? $self->{'.delay'} =
|
---|
107 | $delay : $self->{'.delay'};
|
---|
108 | }
|
---|
109 |
|
---|
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'),
|
---|
128 | h1('Visible'),"\n",
|
---|
129 | "This page has been called ", strong($counter)," times",
|
---|
130 | end_html();
|
---|
131 | }
|
---|
132 |
|
---|
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 | }
|
---|
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 |
|
---|
154 | Only Netscape Navigator supports server push. Internet Explorer
|
---|
155 | browsers do not.
|
---|
156 |
|
---|
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-
|
---|
174 |
|
---|
175 | use CGI::Push qw(:standard);
|
---|
176 | do_push(-next_page=>\&draw_a_page);
|
---|
177 |
|
---|
178 | Parameters are as follows:
|
---|
179 |
|
---|
180 | =over 4
|
---|
181 |
|
---|
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'),
|
---|
199 | "This page called $counter times";
|
---|
200 | }
|
---|
201 |
|
---|
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 |
|
---|
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
|
---|
216 | defaults to "text/html". Normally the module assumes that each page
|
---|
217 | is of a homogenous MIME type. However if you provide either of the
|
---|
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.
|
---|
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 |
|
---|
230 | =item -cookie, -target, -expires, -nph
|
---|
231 |
|
---|
232 | These have the same meaning as the like-named parameters in
|
---|
233 | CGI::header().
|
---|
234 |
|
---|
235 | If not specified, -nph will default to 1 (as needed for many servers, see below).
|
---|
236 |
|
---|
237 | =back
|
---|
238 |
|
---|
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
|
---|
253 | start_html('testing'),
|
---|
254 | h1('testing'),
|
---|
255 | "This page called $counter times";
|
---|
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) = @_;
|
---|
267 | return undef if $counter > 10;
|
---|
268 | return header('text/html'), # note we're producing the header here
|
---|
269 | start_html('testing'),
|
---|
270 | h1('testing'),
|
---|
271 | "This page called $counter times";
|
---|
272 | }
|
---|
273 |
|
---|
274 | sub my_last_page {
|
---|
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;
|
---|
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 |
|
---|
293 | =head1 INSTALLING CGI::Push SCRIPTS
|
---|
294 |
|
---|
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-".
|
---|
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 |
|
---|
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 |
|
---|
307 | =head1 AUTHOR INFORMATION
|
---|
308 |
|
---|
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.
|
---|
313 |
|
---|
314 | Address bug reports and comments to: [email protected]
|
---|
315 |
|
---|
316 | =head1 BUGS
|
---|
317 |
|
---|
318 | This section intentionally left blank.
|
---|
319 |
|
---|
320 | =head1 SEE ALSO
|
---|
321 |
|
---|
322 | L<CGI::Carp>, L<CGI>
|
---|
323 |
|
---|
324 | =cut
|
---|
325 |
|
---|