| 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;
|
|---|
|
|---|