package MultipartFilter;
use v5.12;
use warnings;
sub hookInto # $userAgent, onStart => sub { ... }, onDocument => sub { ... }, onEnd => sub { ... }
{
my ($class,$ua,%args)=@_;
my $onStart=$args{'onStart'}//sub {};
my $onDocument=$args{'onDocument'}//sub {};
my $onEnd=$args{'onEnd'}//sub {};
$ua->add_handler(
response_header => sub {
# my ($response,$ua,$h)=@_;
my ($response,$ua)=@_;
$onStart->($response,$ua);
# Remember how many times we called the onDocument callback
$response->{'.multipartfilter'}=0;
return;
},
m_media_type => "multipart/*"
);
my $flushDocuments=sub {
my ($parts,$response,$ua)=@_;
# get rid of all the parts that we have already processed
for (my $i=0; $i<$response->{'.multipartfilter'}; $i++) {
shift @$parts;
}
# call the onDocument callback for all new parts
for my $part (@$parts) {
$onDocument->($part,$response,$ua);
$response->{'.multipartfilter'}++;
}
};
$ua->add_handler(
response_data => sub {
# my ($response,$ua,$h,$data)=@_;
my ($response,$ua)=@_;
my @parts=$response->parts();
# The last part is special, it may be not yet completely transmitted.
# All other parts are complete, but may still need the onDocument
# callback call:
my $lastpart=pop @parts;
$flushDocuments->(\@parts,$response,$ua);
my $clen=$lastpart->header('Content-Length');
defined($clen) or die "Missing Content-Length header in multipart response";
my $cref=$lastpart->content_ref(); # don't copy possibly large content around
# If the content is shorter than announced, we have not yet
# received all of it, and must not call the callback now.
unless (length($$cref)<$clen) {
$onDocument->($lastpart,$response,$ua);
$response->{'.multipartfilter'}++;
}
return 1;
},
m_media_type => "multipart/*"
);
$ua->add_handler(
response_done => sub {
# my ($response,$ua,$h)=@_;
my ($response,$ua)=@_;
my @parts=$response->parts();
# All parts are complete, including the last one.
# Make sure the callback has been called for all of them.
$flushDocuments->(\@parts,$response,$ua);
$onEnd->($response,$ua);
return;
},
m_media_type => "multipart/*"
);
return;
}
1;
####
#!/usr/bin/perl
use v5.12;
use warnings;
use Data::Dumper;
use LWP::UserAgent;
use MultipartFilter;
$|=1;
my $ua=LWP::UserAgent->new();
MultipartFilter->hookInto(
$ua,
onStart => sub {
say "** Hey, I got a multi-part response!";
},
onDocument => sub {
my ($part,$response,$ua)=@_;
say "** Hey, I got a new document!";
say "Headers are:";
say $part->headers->as_string();
say "Content is:";
say $part->content();
},
onEnd => sub {
say "** End of multipart response.";
}
);
say "*** single-part response ***";
my $resp=$ua->get("http://localhost/~alex/files/server-push/hello.cgi");
say $resp->as_string();
say "*** multi-part response ***";
$resp=$ua->get("http://localhost/~alex/files/server-push/push-server.cgi");
say $resp->as_string();
##
##
#!/usr/bin/perl
use v5.12;
use warnings;
$|=1;
print
"Status: 200 OK\r\n",
"Content-Type: text/plain\r\n",
"\r\n",
"Hello World!\r\n";
##
##
#!/usr/bin/perl
use v5.12;
use warnings;
$|=1;
my $boundary=join('-','cut','here',$$,time(),rand());
print
"Status: 200 OK\r\n",
"MIME-Version: 1.0\r\n",
"Content-Type: multipart/mixed; boundary=\"$boundary\"\r\n",
"\r\n";
for (1..3) {
my $now=localtime;
my $text1=<<__END_OF_HTML__;
Testing multipart/mixed
Testing multipart/mixed
__END_OF_HTML__
my $text2=<<__END_OF_HTML__;
- Counter
- $_
- Server time
- $now
__END_OF_HTML__
print
"--$boundary\r\n",
"Content-Type: text/html; charset=windows-1252\r\n",
"Content-Length: ",length("$text1$text2"),"\r\n",
"X-My-Counter: $_\r\n",
"\r\n",
$text1;
sleep 1;
print $text2;
sleep 1;
}
print
"--$boundary--\r\n";
##
##
MultipartFilter->hookInto($browser,
onDocument => sub {
my $part=shift;
$twig->parse($part->content());
}
);