Content Delivery Network (CDN) - optional, for either uploads only or both uploads & extras (rfe 9134)
This commit is contained in:
parent
d6696f8a7e
commit
acd3fded45
8 changed files with 646 additions and 11 deletions
|
|
@ -991,5 +991,19 @@
|
||||||
"WebGUI::Shop::TaxDriver::EU"
|
"WebGUI::Shop::TaxDriver::EU"
|
||||||
],
|
],
|
||||||
|
|
||||||
}
|
# Content Delivery Network - for use with WebGUI::Storage
|
||||||
|
# required for CDN: enabled, url, queuePath, syncProgram, deleteProgram
|
||||||
|
# optional for CDN: sslAlt, sslUrl, extrasCdn, extrasSsl, extrasExclude
|
||||||
|
"cdn" : { "enabled" : 0,
|
||||||
|
"url" : "http://content.example.com",
|
||||||
|
"sslAlt" : 0,
|
||||||
|
"sslUrl" : "https://ssl.example.com",
|
||||||
|
"queuePath" : "/data/cdnqueue",
|
||||||
|
"syncProgram" : "/usr/bin/rsync -av --chmod=u+rwx -- '%s' user@content.example.com:/path",
|
||||||
|
"deleteProgram" : "/usr/bin/ssh user@content.example.com 'rm -Rf -- %s'"
|
||||||
|
"extrasCdn" : "http://content.example.com/extras",
|
||||||
|
"extrasSsl" : "https://content.example.com/extras",
|
||||||
|
"extrasExclude": ["tinymce", "^blah$"]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,28 @@ These methods are available from this class:
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 fromHex ( hexId )
|
||||||
|
|
||||||
|
Returns the guid corresponding to hexId. Converse of toHex.
|
||||||
|
|
||||||
|
=head3 hexId
|
||||||
|
|
||||||
|
Hex value to convert to guid.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub fromHex {
|
||||||
|
my $self = shift;
|
||||||
|
my $hexId = shift;
|
||||||
|
my $binId = pack('H2' x 16, unpack('A2' x 16, $hexId));
|
||||||
|
my $id = substr(encode_base64($binId), 0, 22);
|
||||||
|
$id =~ tr{+/}{_-};
|
||||||
|
return $id;
|
||||||
|
}
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
=head2 DESTROY ( )
|
=head2 DESTROY ( )
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ sub escape {
|
||||||
|
|
||||||
=head2 extras ( path )
|
=head2 extras ( path )
|
||||||
|
|
||||||
Combinds the base extrasURL defined in the config file with a specfied path.
|
Combines the base extrasURL defined in the config file with a specified path.
|
||||||
|
|
||||||
=head3 path
|
=head3 path
|
||||||
|
|
||||||
|
|
@ -144,7 +144,20 @@ consecutive slashes in the path part of the URL will be replaced with a single s
|
||||||
sub extras {
|
sub extras {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $path = shift;
|
my $path = shift;
|
||||||
my $url = $self->session->config->get("extrasURL").'/'.$path;
|
my $url = $self->session->config->get("extrasURL");
|
||||||
|
my $cdnCfg = $self->session->config->get('cdn');
|
||||||
|
if ($cdnCfg and $cdnCfg->{'enabled'} and $cdnCfg->{'extrasCdn'}) {
|
||||||
|
unless ($path and grep $path =~ m/$_/, @{$cdnCfg->{'extrasExclude'}}) {
|
||||||
|
if ($cdnCfg->{'extrasSsl'} and
|
||||||
|
($self->session->env->get('HTTPS') eq 'on' or
|
||||||
|
$self->session->env->get('SSLPROXY'))) {
|
||||||
|
$url = $cdnCfg->{'extrasSsl'};
|
||||||
|
} else {
|
||||||
|
$url = $cdnCfg->{'extrasCdn'};
|
||||||
|
}
|
||||||
|
} # if excluded, stick with regular extrasURL
|
||||||
|
}
|
||||||
|
$url .= '/' . $path;
|
||||||
$url =~ s$(?<!:)/{2,}$/$g; ##Remove //, unless it's after a :, which can't be a valid URL character
|
$url =~ s$(?<!:)/{2,}$/$g; ##Remove //, unless it's after a :, which can't be a valid URL character
|
||||||
return $url;
|
return $url;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,75 @@ sub _addError {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 _cdnAdd ( )
|
||||||
|
|
||||||
|
Adds to CDN queue, for any of the add* methods.
|
||||||
|
|
||||||
|
NOTE: This is a private method and should never be called except internally to this package.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub _cdnAdd {
|
||||||
|
my $self = shift;
|
||||||
|
my $cdnCfg = $self->session->config->get('cdn');
|
||||||
|
if ($cdnCfg and $cdnCfg->{'enabled'}) {
|
||||||
|
if ($cdnCfg->{'queuePath'}) {
|
||||||
|
my $cdnFile = $cdnCfg->{'queuePath'} . '/' . $self->session->id->toHex($self->getId);
|
||||||
|
my $dest;
|
||||||
|
if ( open $dest, '>', $cdnFile ) {
|
||||||
|
close $dest; # created empty file
|
||||||
|
} else {
|
||||||
|
$self->_addError("CDN: Couldn't open file $cdnFile for writing due to error: ".$!);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$self->_addError('Invalid CDN configuration - missing queuePath');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 _cdnDel ( [delDotCdn] )
|
||||||
|
|
||||||
|
Add file denoting deletion to the CDN queue, for the clear & delete methods.
|
||||||
|
|
||||||
|
NOTE: This is a private method and should never be called except internally to this package.
|
||||||
|
|
||||||
|
=head3 delDotCdn
|
||||||
|
|
||||||
|
Delete the ".cdn" file - clear vs. delete.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub _cdnDel {
|
||||||
|
my $self = shift;
|
||||||
|
my $delDotCdn = shift;
|
||||||
|
my $cdnCfg = $self->session->config->get('cdn');
|
||||||
|
if ($cdnCfg and $cdnCfg->{'enabled'}) {
|
||||||
|
my $cdnFile; # path/name of flag and/or queue file
|
||||||
|
if ($delDotCdn) {
|
||||||
|
$cdnFile = $self->getPath . '/.cdn';
|
||||||
|
unlink $cdnFile;
|
||||||
|
}
|
||||||
|
if ($cdnCfg->{'queuePath'}) {
|
||||||
|
$cdnFile = $cdnCfg->{'queuePath'} . '/' . $self->session->id->toHex($self->getId);
|
||||||
|
my $dest;
|
||||||
|
if ( open $dest, '>', $cdnFile ) {
|
||||||
|
print $dest "deleted\n";
|
||||||
|
close $dest;
|
||||||
|
} else {
|
||||||
|
$self->_addError("Couldn't open file $cdnFile for writing due to error: ".$!);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$self->_addError('Invalid CDN configuration - missing queuePath');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
=head2 _makePath ( )
|
=head2 _makePath ( )
|
||||||
|
|
@ -158,6 +227,8 @@ sub _changeOwner {
|
||||||
|
|
||||||
Generates a captcha image (200x x 50px) and returns the filename and challenge string (6 random characters). For more information about captcha, consult the Wikipedia here: http://en.wikipedia.org/wiki/Captcha
|
Generates a captcha image (200x x 50px) and returns the filename and challenge string (6 random characters). For more information about captcha, consult the Wikipedia here: http://en.wikipedia.org/wiki/Captcha
|
||||||
|
|
||||||
|
Note: captcha images will NOT be synchronized to a CDN, even if other files are.
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
sub addFileFromCaptcha {
|
sub addFileFromCaptcha {
|
||||||
|
|
@ -212,6 +283,7 @@ sub addFileFromCaptcha {
|
||||||
=head2 addFileFromFilesystem( pathToFile )
|
=head2 addFileFromFilesystem( pathToFile )
|
||||||
|
|
||||||
Grabs a file from the server's file system and saves it to a storage location and returns a URL compliant filename. If there are errors encountered during the add, then it will return undef instead.
|
Grabs a file from the server's file system and saves it to a storage location and returns a URL compliant filename. If there are errors encountered during the add, then it will return undef instead.
|
||||||
|
If configured for CDN, add this storage location to CDN queue.
|
||||||
|
|
||||||
=head3 pathToFile
|
=head3 pathToFile
|
||||||
|
|
||||||
|
|
@ -256,6 +328,7 @@ sub addFileFromFilesystem {
|
||||||
or $self->_addError("Couldn't copy $pathToFile to ".$self->getPath($filename).": $!");
|
or $self->_addError("Couldn't copy $pathToFile to ".$self->getPath($filename).": $!");
|
||||||
close $dest;
|
close $dest;
|
||||||
close $source;
|
close $source;
|
||||||
|
$self->_cdnAdd;
|
||||||
return $filename;
|
return $filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -265,6 +338,7 @@ sub addFileFromFilesystem {
|
||||||
=head2 addFileFromFormPost ( formVariableName, attachmentLimit )
|
=head2 addFileFromFormPost ( formVariableName, attachmentLimit )
|
||||||
|
|
||||||
Grabs an attachment from a form POST and saves it to this storage location.
|
Grabs an attachment from a form POST and saves it to this storage location.
|
||||||
|
If configured for CDN, add this storage location to CDN queue.
|
||||||
|
|
||||||
=head3 formVariableName
|
=head3 formVariableName
|
||||||
|
|
||||||
|
|
@ -289,8 +363,10 @@ sub addFileFromFormPost {
|
||||||
my $attachmentCount = 1;
|
my $attachmentCount = 1;
|
||||||
foreach my $upload ($session->request->upload($formVariableName)) {
|
foreach my $upload ($session->request->upload($formVariableName)) {
|
||||||
$session->errorHandler->info("Trying to get " . $upload->filename." from ".$formVariableName);
|
$session->errorHandler->info("Trying to get " . $upload->filename." from ".$formVariableName);
|
||||||
return $filename
|
if ($attachmentCount > $attachmentLimit) {
|
||||||
if $attachmentCount > $attachmentLimit;
|
$self->_cdnAdd;
|
||||||
|
return $filename;
|
||||||
|
}
|
||||||
my $clientFilename = $upload->filename;
|
my $clientFilename = $upload->filename;
|
||||||
next
|
next
|
||||||
unless $clientFilename;
|
unless $clientFilename;
|
||||||
|
|
@ -316,6 +392,7 @@ sub addFileFromFormPost {
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$filename and $self->_cdnAdd;
|
||||||
return $filename;
|
return $filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -325,6 +402,7 @@ sub addFileFromFormPost {
|
||||||
=head2 addFileFromHashref ( filename, hashref )
|
=head2 addFileFromHashref ( filename, hashref )
|
||||||
|
|
||||||
Stores a hash reference as a file and returns a URL compliant filename. Retrieve the data with getFileContentsAsHashref.
|
Stores a hash reference as a file and returns a URL compliant filename. Retrieve the data with getFileContentsAsHashref.
|
||||||
|
If configured for CDN, add this storage location to CDN queue.
|
||||||
|
|
||||||
=head3 filename
|
=head3 filename
|
||||||
|
|
||||||
|
|
@ -343,6 +421,7 @@ sub addFileFromHashref {
|
||||||
Storable::nstore($hashref, $self->getPath($filename))
|
Storable::nstore($hashref, $self->getPath($filename))
|
||||||
or $self->_addError("Couldn't create file ".$self->getPath($filename)." because ".$!);
|
or $self->_addError("Couldn't create file ".$self->getPath($filename)." because ".$!);
|
||||||
$self->_changeOwner($self->getPath($filename));
|
$self->_changeOwner($self->getPath($filename));
|
||||||
|
$filename and $self->_cdnAdd;
|
||||||
return $filename;
|
return $filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -351,6 +430,7 @@ sub addFileFromHashref {
|
||||||
=head2 addFileFromScalar ( filename, content )
|
=head2 addFileFromScalar ( filename, content )
|
||||||
|
|
||||||
Adds a file to this storage location and returns a URL compliant filename.
|
Adds a file to this storage location and returns a URL compliant filename.
|
||||||
|
If configured for CDN, add this storage location to CDN queue.
|
||||||
|
|
||||||
=head3 filename
|
=head3 filename
|
||||||
|
|
||||||
|
|
@ -373,6 +453,7 @@ sub addFileFromScalar {
|
||||||
print $FILE $content;
|
print $FILE $content;
|
||||||
close($FILE);
|
close($FILE);
|
||||||
$self->_changeOwner($self->getPath($filename));
|
$self->_changeOwner($self->getPath($filename));
|
||||||
|
$self->_cdnAdd;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$self->_addError("Couldn't create file ".$self->getPath($filename)." because ".$!);
|
$self->_addError("Couldn't create file ".$self->getPath($filename)." because ".$!);
|
||||||
|
|
@ -415,6 +496,7 @@ sub adjustMaxImageSize {
|
||||||
=head2 clear ( )
|
=head2 clear ( )
|
||||||
|
|
||||||
Clears a storage locations of all files except the .wgaccess file
|
Clears a storage locations of all files except the .wgaccess file
|
||||||
|
If configured for CDN, add deletion of this location's files, to CDN queue.
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
|
|
@ -424,6 +506,7 @@ sub clear {
|
||||||
foreach my $file (@{$filelist}) {
|
foreach my $file (@{$filelist}) {
|
||||||
$self->deleteFile($file);
|
$self->deleteFile($file);
|
||||||
}
|
}
|
||||||
|
$self->_cdnDel(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -431,7 +514,8 @@ sub clear {
|
||||||
|
|
||||||
=head2 copy ( [ storage, filelist ] )
|
=head2 copy ( [ storage, filelist ] )
|
||||||
|
|
||||||
Copies a storage location and it's contents. Returns a new storage location object. Note that this does not copy privileges or other special filesystem properties.
|
Copies a storage location and its contents. Returns a new storage location object. Note that this does not copy privileges or other special filesystem properties.
|
||||||
|
If configured for CDN, add the resulting new storage location to CDN queue.
|
||||||
|
|
||||||
=head3 storage
|
=head3 storage
|
||||||
|
|
||||||
|
|
@ -448,6 +532,7 @@ sub copy {
|
||||||
my $newStorage = shift || WebGUI::Storage->create($self->session);
|
my $newStorage = shift || WebGUI::Storage->create($self->session);
|
||||||
my $filelist = shift || $self->getFiles(1);
|
my $filelist = shift || $self->getFiles(1);
|
||||||
foreach my $file (@{$filelist}) {
|
foreach my $file (@{$filelist}) {
|
||||||
|
next if $file eq '.cdn';
|
||||||
open my $source, '<:raw', $self->getPath($file) or next;
|
open my $source, '<:raw', $self->getPath($file) or next;
|
||||||
open my $dest, '>:raw', $newStorage->getPath($file) or next;
|
open my $dest, '>:raw', $newStorage->getPath($file) or next;
|
||||||
File::Copy::copy($source, $dest) or $self->_addError("Couldn't copy file ".$self->getPath($file)." to ".$newStorage->getPath($file)." because ".$!);
|
File::Copy::copy($source, $dest) or $self->_addError("Couldn't copy file ".$self->getPath($file)." to ".$newStorage->getPath($file)." because ".$!);
|
||||||
|
|
@ -455,6 +540,7 @@ sub copy {
|
||||||
close $source;
|
close $source;
|
||||||
$newStorage->_changeOwner($newStorage->getPath($file));
|
$newStorage->_changeOwner($newStorage->getPath($file));
|
||||||
}
|
}
|
||||||
|
$newStorage->_cdnAdd;
|
||||||
return $newStorage;
|
return $newStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -464,6 +550,7 @@ sub copy {
|
||||||
|
|
||||||
Copy a file in this storage location. C<filename> is the file to copy.
|
Copy a file in this storage location. C<filename> is the file to copy.
|
||||||
C<newFilename> is the new file to create.
|
C<newFilename> is the new file to create.
|
||||||
|
If configured for CDN, add this storage location to CDN queue.
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
|
|
@ -481,6 +568,7 @@ sub copyFile {
|
||||||
|| croak "Couldn't copy '$filename' to '$newFilename': $!";
|
|| croak "Couldn't copy '$filename' to '$newFilename': $!";
|
||||||
$self->_changeOwner($self->getPath($filename));
|
$self->_changeOwner($self->getPath($filename));
|
||||||
|
|
||||||
|
$self->_cdnAdd;
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -539,6 +627,7 @@ sub createTemp {
|
||||||
=head2 delete ( )
|
=head2 delete ( )
|
||||||
|
|
||||||
Deletes this storage location and its contents (if any) from the filesystem.
|
Deletes this storage location and its contents (if any) from the filesystem.
|
||||||
|
If configured for CDN, add deletion of this storage location to CDN queue.
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
|
|
@ -554,6 +643,8 @@ sub delete {
|
||||||
# can only remove empty directories, will fail silently otherwise
|
# can only remove empty directories, will fail silently otherwise
|
||||||
rmdir $fullPath;
|
rmdir $fullPath;
|
||||||
}
|
}
|
||||||
|
# Delete the content from the CDN - enqueue
|
||||||
|
$self->_cdnDel(0);
|
||||||
$self->session->errorHandler->info("Deleted storage ".$self->getId);
|
$self->session->errorHandler->info("Deleted storage ".$self->getId);
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
@ -562,7 +653,7 @@ sub delete {
|
||||||
|
|
||||||
=head2 deleteFile ( filename )
|
=head2 deleteFile ( filename )
|
||||||
|
|
||||||
Deletes a file from it's storage location.
|
Deletes a file from its storage location.
|
||||||
|
|
||||||
=head3 filename
|
=head3 filename
|
||||||
|
|
||||||
|
|
@ -581,6 +672,38 @@ sub deleteFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 deleteFromCdn ( )
|
||||||
|
|
||||||
|
Run config/cdn/deleteProgram to synchronize this location to Content Delivery Network.
|
||||||
|
Replace %s with the path of the storage location.
|
||||||
|
Also delete the related file in config/cdn/queuePath.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub deleteFromCdn {
|
||||||
|
my $self = shift;
|
||||||
|
my $cdnCfg = $self->session->config->get('cdn');
|
||||||
|
if ($cdnCfg and $cdnCfg->{'enabled'}
|
||||||
|
and $cdnCfg->{'syncProgram'}) {
|
||||||
|
my $id = $self->session->id->toHex($self->getId);
|
||||||
|
my $cmd = sprintf($cdnCfg->{'deleteProgram'}, $id);
|
||||||
|
if ($cmd =~ /$id/) { # sanity check, no rm -rf /
|
||||||
|
system($cmd);
|
||||||
|
if ($?) { # This may occur benign in the case delete after clear
|
||||||
|
$self->_addError("Error running CDN deleteProgram: $?");
|
||||||
|
}
|
||||||
|
if ($cdnCfg->{'queuePath'}) {
|
||||||
|
unlink $cdnCfg->{'queuePath'} . '/' . $self->session->id->toHex($self->getId);
|
||||||
|
}
|
||||||
|
} else { # Presume configuration error, missing %s
|
||||||
|
$self->_addError("CDN deleteProgram: storage ID missing from command: $cmd");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
=head2 get ( session, id )
|
=head2 get ( session, id )
|
||||||
|
|
@ -676,6 +799,46 @@ sub generateThumbnail {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 getCdnFileIterator ( session )
|
||||||
|
|
||||||
|
Class method to return an iterator method full of storage objects to
|
||||||
|
be updated or deleted, based upon what's in config/cdn/queuePath.
|
||||||
|
|
||||||
|
=head3 session
|
||||||
|
|
||||||
|
A reference to the current session.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub getCdnFileIterator {
|
||||||
|
my $class = shift;
|
||||||
|
my $session = shift;
|
||||||
|
my $cdnCfg = $session->config->get('cdn');
|
||||||
|
if ($cdnCfg and $cdnCfg->{'enabled'}) {
|
||||||
|
if ($cdnCfg->{'queuePath'}) {
|
||||||
|
if (opendir my $DH, $cdnCfg->{'queuePath'}) {
|
||||||
|
my @ids = grep { !/^\.+$/ }
|
||||||
|
readdir($DH);
|
||||||
|
close $DH;
|
||||||
|
my $sub = sub {
|
||||||
|
my $id = shift @ids;
|
||||||
|
return if !$id;
|
||||||
|
return $class->get($session, $session->id->fromHex($id));
|
||||||
|
};
|
||||||
|
return $sub;
|
||||||
|
} else {
|
||||||
|
$session->errorHandler->warn("CDN: cannot read directory $cdnCfg->{'queuePath'}");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$session->errorHandler->warn("CDN: enabled but no queuePath");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
=head2 getSize ( filename )
|
=head2 getSize ( filename )
|
||||||
|
|
@ -702,6 +865,7 @@ sub getSize {
|
||||||
return($x, $y);
|
return($x, $y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
=head2 getErrorCount ( )
|
=head2 getErrorCount ( )
|
||||||
|
|
@ -1026,6 +1190,22 @@ sub getUrl {
|
||||||
my $url = $self->session->config->get("uploadsURL")
|
my $url = $self->session->config->get("uploadsURL")
|
||||||
. '/'
|
. '/'
|
||||||
. $self->getPathFrag;
|
. $self->getPathFrag;
|
||||||
|
my $cdnCfg = $self->session->config->get('cdn');
|
||||||
|
if ($cdnCfg and $cdnCfg->{'enabled'} and $cdnCfg->{'url'}
|
||||||
|
and -e $self->getPath . '/.cdn') {
|
||||||
|
my $sep = '/'; # separator, if not already present trailing
|
||||||
|
if ($cdnCfg->{'sslAlt'} and
|
||||||
|
($self->session->env->get('HTTPS') eq 'on' or
|
||||||
|
$self->session->env->get('SSLPROXY'))) {
|
||||||
|
if ($cdnCfg->{'sslUrl'}) {
|
||||||
|
substr($cdnCfg->{'sslUrl'}, -1) eq '/' and $sep = '';
|
||||||
|
$url = $cdnCfg->{'sslUrl'} . $sep . $self->session->id->toHex($self->getId);
|
||||||
|
} # else do NOT override $url with CDN URL ($url = $sslUrl || $url)
|
||||||
|
} else {
|
||||||
|
substr($cdnCfg->{'url'}, -1) eq '/' and $sep = '';
|
||||||
|
$url = $cdnCfg->{'url'} . $sep . $self->session->id->toHex($self->getId);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (defined $file) {
|
if (defined $file) {
|
||||||
$url .= '/'.$file;
|
$url .= '/'.$file;
|
||||||
}
|
}
|
||||||
|
|
@ -1408,6 +1588,43 @@ sub setPrivileges {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 syncToCdn ( )
|
||||||
|
|
||||||
|
Run config/cdn/syncProgram to synchronize this location to Content Delivery Network.
|
||||||
|
Replace %s with the path of the storage location.
|
||||||
|
Also put an empty ".cdn" file in the storage location, and then delete
|
||||||
|
the related file in config/cdn/queuePath.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub syncToCdn {
|
||||||
|
my $self = shift;
|
||||||
|
my $cdnCfg = $self->session->config->get('cdn');
|
||||||
|
if ($cdnCfg and $cdnCfg->{'enabled'}
|
||||||
|
and $cdnCfg->{'syncProgram'}) {
|
||||||
|
my $originalDir = Cwd::cwd();
|
||||||
|
my $locDir = join '/', $self->session->config->get('uploadsPath'), @{$self->{_pathParts}}[0..1];
|
||||||
|
chdir $locDir or croak 'Unable to chdir to ' . $locDir . " : $!";
|
||||||
|
my $cmd = sprintf($cdnCfg->{'syncProgram'}, $self->session->id->toHex($self->getId));
|
||||||
|
system($cmd);
|
||||||
|
if ($?) {
|
||||||
|
$self->_addError("Error running CDN syncProgram: $?");
|
||||||
|
} elsif ($cdnCfg->{'queuePath'}) {
|
||||||
|
unlink $cdnCfg->{'queuePath'} . '/' . $self->session->id->toHex($self->getId);
|
||||||
|
}
|
||||||
|
chdir $originalDir;
|
||||||
|
my $dest;
|
||||||
|
my $cdnFile = $self->getPath . '/.cdn';
|
||||||
|
if ( open $dest, '>', $cdnFile ) {
|
||||||
|
close $dest; # created empty file
|
||||||
|
} else {
|
||||||
|
$self->_addError("Couldn't open file $cdnFile for writing due to error: ".$!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
||||||
206
sbin/syncToCdn.pl
Normal file
206
sbin/syncToCdn.pl
Normal file
|
|
@ -0,0 +1,206 @@
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
# Please read the legal notices (docs/legal.txt) and the license
|
||||||
|
# (docs/license.txt) that came with this distribution before using
|
||||||
|
# this software.
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
# http://www.plainblack.com info@plainblack.com
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
|
our $webguiRoot;
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
$webguiRoot = "..";
|
||||||
|
unshift (@INC, $webguiRoot."/lib");
|
||||||
|
}
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use Fcntl ':flock';
|
||||||
|
use Getopt::Long;
|
||||||
|
use WebGUI::Session;
|
||||||
|
use WebGUI::Storage;
|
||||||
|
|
||||||
|
my $configFile;
|
||||||
|
my $help;
|
||||||
|
my $migrate;
|
||||||
|
my $override;
|
||||||
|
my $quiet;
|
||||||
|
|
||||||
|
GetOptions(
|
||||||
|
'configFile=s' => \$configFile,
|
||||||
|
'override' => \$override,
|
||||||
|
'migrate' => \$migrate,
|
||||||
|
'quiet' => \$quiet,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
if ($configFile eq "") {
|
||||||
|
printHelp();
|
||||||
|
exit 4;
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($help) {
|
||||||
|
printHelp();
|
||||||
|
exit 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
# don't want two copies of this to run simultaneously
|
||||||
|
unless (flock(DATA, LOCK_EX|LOCK_NB)) {
|
||||||
|
print "$0 is already running. Exiting.\n";
|
||||||
|
exit 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub printHelp {
|
||||||
|
print <<STOP;
|
||||||
|
|
||||||
|
Usage: perl $0 --configfile=<webguiConfig>
|
||||||
|
|
||||||
|
--configFile WebGUI config file.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
--override This utility is designed to be run as
|
||||||
|
a privileged user on Linux style systems.
|
||||||
|
If you wish to run this utility without
|
||||||
|
being the super user, then use this flag,
|
||||||
|
but note that it may not work as
|
||||||
|
intended.
|
||||||
|
|
||||||
|
--migrate Migrate entirety of uploads directory to CDN.
|
||||||
|
Ignore the CDN queue and sync everything.
|
||||||
|
|
||||||
|
--help Display this help message and exit.
|
||||||
|
|
||||||
|
--quiet Disable output unless there's an error.
|
||||||
|
|
||||||
|
EXIT STATUS
|
||||||
|
|
||||||
|
The following exit values are returned:
|
||||||
|
|
||||||
|
0
|
||||||
|
Successful execution.
|
||||||
|
|
||||||
|
1
|
||||||
|
Only super user may run the script.
|
||||||
|
|
||||||
|
2
|
||||||
|
Help requested.
|
||||||
|
|
||||||
|
3
|
||||||
|
Only one instance of this script can run at a time.
|
||||||
|
|
||||||
|
4
|
||||||
|
Error during invocation of the command.
|
||||||
|
|
||||||
|
5
|
||||||
|
Content Delivery Network (CDN) is not enabled.
|
||||||
|
|
||||||
|
STOP
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!($^O =~ /^Win/i) && $> != 0 && !$override) {
|
||||||
|
print "You must be the super user to use this utility.\n";
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
print "Starting..." unless ($quiet);
|
||||||
|
my $session = WebGUI::Session->open($webguiRoot,$configFile);
|
||||||
|
$session->user({userId=>3});
|
||||||
|
print "OK\n" unless ($quiet);
|
||||||
|
|
||||||
|
my $cdnCfg = $session->config->get('cdn');
|
||||||
|
unless ($cdnCfg and $cdnCfg->{'enabled'} and $cdnCfg->{'queuePath'}) {
|
||||||
|
print "Content delivery network (CDN) is not enabled in $configFile.\n";
|
||||||
|
exit 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Here is the core of the script
|
||||||
|
if ($migrate) {
|
||||||
|
syncUploads($session);
|
||||||
|
} else {
|
||||||
|
syncQueue($session, $cdnCfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
print "Cleaning up..." unless ($quiet);
|
||||||
|
$session->var->end();
|
||||||
|
$session->close();
|
||||||
|
|
||||||
|
print "OK\n" unless ($quiet);
|
||||||
|
exit 0;
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------
|
||||||
|
# syncQueue(session, cdnConfig)
|
||||||
|
#-----------------------------------------
|
||||||
|
|
||||||
|
sub syncQueue {
|
||||||
|
my $session = shift;
|
||||||
|
my $cdnCfg = shift;
|
||||||
|
my $locIter = WebGUI::Storage->getCdnFileIterator($session);
|
||||||
|
while (my $store = $locIter->()) {
|
||||||
|
my $ctrlFile = $cdnCfg->{'queuePath'} . '/' . $session->id->toHex($store->getId);
|
||||||
|
if (-r $ctrlFile and -s $ctrlFile < 12) {
|
||||||
|
if (! -s $ctrlFile) { # Empty means sync/add/update
|
||||||
|
$store->syncToCdn;
|
||||||
|
} else { # expect "deleted" but be careful.
|
||||||
|
if (open my $ctrlFH, "<$ctrlFile") {
|
||||||
|
my $directive = <$ctrlFH>;
|
||||||
|
chomp $directive;
|
||||||
|
close $ctrlFH;
|
||||||
|
if ($directive =~ m/^deleted$/i) {
|
||||||
|
$store->deleteFromCdn;
|
||||||
|
} # else unknown - ignore
|
||||||
|
} else {
|
||||||
|
warn "Cannot read CDN control file $ctrlFile.";
|
||||||
|
$session->errorHandler->warn("Cannot read CDN control file $ctrlFile.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { # missing or invalid
|
||||||
|
print "No recognizable CDN control file $ctrlFile.\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} # end syncQueue
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------
|
||||||
|
# syncUploads(session)
|
||||||
|
#-----------------------------------------
|
||||||
|
|
||||||
|
sub syncUploads {
|
||||||
|
my $session = shift;
|
||||||
|
# Alternate approach would be touch queue files, then run queue.
|
||||||
|
my $uDir = $session->config->get('uploadsPath');
|
||||||
|
if (opendir my $DH, $uDir) {
|
||||||
|
my @part1 = grep { !/^\.+$/ } readdir($DH);
|
||||||
|
foreach my $subdir (@part1) {
|
||||||
|
if (opendir my $SD, "$uDir/$subdir") {
|
||||||
|
my @part2 = grep { !/^\.+$/ } readdir($SD);
|
||||||
|
foreach my $sub2 (@part2) {
|
||||||
|
if (opendir my $S2, "$uDir/$subdir/$sub2") {
|
||||||
|
my @fileId = grep { !/^\.+$/ } readdir($S2);
|
||||||
|
foreach my $fileId (@fileId) {
|
||||||
|
my $store = WebGUI::Storage->get($session,
|
||||||
|
$session->id->fromHex($fileId));
|
||||||
|
$store->syncToCdn; # here is the meat
|
||||||
|
}
|
||||||
|
close $S2;
|
||||||
|
} else {
|
||||||
|
$session->errorHandler->warn("Unable to open $sub2 for directory reading");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close $SD;
|
||||||
|
} else {
|
||||||
|
$session->errorHandler->warn("Unable to open $subdir for directory reading");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close $DH;
|
||||||
|
} else {
|
||||||
|
$session->errorHandler->warn("Unable to open $uDir for directory reading");
|
||||||
|
}
|
||||||
|
} # end syncUploads
|
||||||
|
|
||||||
|
|
||||||
|
__DATA__
|
||||||
|
This exists so flock() code above works.
|
||||||
|
DO NOT REMOVE THIS DATA SECTION.
|
||||||
|
|
@ -53,7 +53,7 @@ my @testSets = (
|
||||||
|
|
||||||
my $session = WebGUI::Test->session;
|
my $session = WebGUI::Test->session;
|
||||||
|
|
||||||
plan tests => scalar(@testSets) + 5;
|
plan tests => scalar(@testSets) + 6;
|
||||||
|
|
||||||
# generate
|
# generate
|
||||||
my $generateId = $session->id->generate();
|
my $generateId = $session->id->generate();
|
||||||
|
|
@ -79,6 +79,7 @@ foreach my $testSet (@testSets) {
|
||||||
#
|
#
|
||||||
|
|
||||||
is($session->id->toHex('wjabZsKOb7kBBSiO3bQwzA'), 'c2369b66c28e6fb90105288eddb430cc', 'toHex works');
|
is($session->id->toHex('wjabZsKOb7kBBSiO3bQwzA'), 'c2369b66c28e6fb90105288eddb430cc', 'toHex works');
|
||||||
|
is($session->id->fromHex('c2369b66c28e6fb90105288eddb430cc'), 'wjabZsKOb7kBBSiO3bQwzA', 'fromHex works');
|
||||||
|
|
||||||
my $re = $session->id->getValidator;
|
my $re = $session->id->getValidator;
|
||||||
is( ref $re, 'Regexp', 'getValidator returns a regexp object');
|
is( ref $re, 'Regexp', 'getValidator returns a regexp object');
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ my @getRefererUrlTests = (
|
||||||
|
|
||||||
use Test::More;
|
use Test::More;
|
||||||
use Test::MockObject::Extends;
|
use Test::MockObject::Extends;
|
||||||
plan tests => 72 + scalar(@getRefererUrlTests);
|
plan tests => 76 + scalar(@getRefererUrlTests);
|
||||||
|
|
||||||
my $session = WebGUI::Test->session;
|
my $session = WebGUI::Test->session;
|
||||||
|
|
||||||
|
|
@ -284,6 +284,12 @@ is($session->url->makeAbsolute('page1'), '/page1', 'makeAbsolute: default baseUr
|
||||||
my $origExtras = $session->config->get('extrasURL');
|
my $origExtras = $session->config->get('extrasURL');
|
||||||
my $extras = $origExtras;
|
my $extras = $origExtras;
|
||||||
|
|
||||||
|
my $savecdn = $session->config->get('cdn');
|
||||||
|
if ($savecdn) {
|
||||||
|
$session->config->delete('cdn');
|
||||||
|
}
|
||||||
|
# Note: the CDN configuration will be reverted in the END
|
||||||
|
|
||||||
is($session->url->extras, $extras.'/', 'extras method returns URL to extras with a trailing slash');
|
is($session->url->extras, $extras.'/', 'extras method returns URL to extras with a trailing slash');
|
||||||
is($session->url->extras('foo.html'), join('/', $extras,'foo.html'), 'extras method appends to the extras url');
|
is($session->url->extras('foo.html'), join('/', $extras,'foo.html'), 'extras method appends to the extras url');
|
||||||
is($session->url->extras('/foo.html'), join('/', $extras,'foo.html'), 'extras method removes extra slashes');
|
is($session->url->extras('/foo.html'), join('/', $extras,'foo.html'), 'extras method removes extra slashes');
|
||||||
|
|
@ -301,8 +307,34 @@ $session->config->set('extrasURL', $extras);
|
||||||
is($session->url->extras('/foo.html'), join('', $extras,'foo.html'), 'extras method removes extra slashes');
|
is($session->url->extras('/foo.html'), join('', $extras,'foo.html'), 'extras method removes extra slashes');
|
||||||
is($session->url->extras('/dir1//foo.html'), join('', $extras,'dir1/foo.html'), 'extras method removes extra slashes anywhere');
|
is($session->url->extras('/dir1//foo.html'), join('', $extras,'dir1/foo.html'), 'extras method removes extra slashes anywhere');
|
||||||
|
|
||||||
|
$extras = 'http://mydomain.com/';
|
||||||
|
$session->config->set('extrasURL', $extras);
|
||||||
|
|
||||||
|
my $cdnCfg = { "enabled" => 1,
|
||||||
|
"extrasCdn" => "http://extras.example.com/",
|
||||||
|
"extrasSsl" => "https://ssl.example.com/",
|
||||||
|
"extrasExclude" => ["^tiny"]
|
||||||
|
};
|
||||||
|
$session->config->set('cdn', $cdnCfg);
|
||||||
|
is($session->url->extras('/dir1/foo.html'), join('', $cdnCfg->{extrasCdn}, 'dir1/foo.html'),
|
||||||
|
'extras cleartext with CDN');
|
||||||
|
is($session->url->extras('tinymce'), join('', $extras, 'tinymce'),
|
||||||
|
'extras exclusion from CDN');
|
||||||
|
# Note: env is already mocked above.
|
||||||
|
$mockEnv{HTTPS} = 'on';
|
||||||
|
is($session->url->extras('/dir1/foo.html'), join('', $cdnCfg->{extrasSsl}, 'dir1/foo.html'),
|
||||||
|
'extras using extrasSsl with HTTPS');
|
||||||
|
$mockEnv{HTTPS} = undef;
|
||||||
|
$mockEnv{SSLPROXY} = 1;
|
||||||
|
is($session->url->extras('/dir1/foo.html'), join('', $cdnCfg->{extrasSsl}, 'dir1/foo.html'),
|
||||||
|
'extras using extrasSsl with SSLPROXY');
|
||||||
|
delete $mockEnv{SSLPROXY};
|
||||||
|
|
||||||
$session->config->set('extrasURL', $origExtras);
|
$session->config->set('extrasURL', $origExtras);
|
||||||
|
|
||||||
|
# partial cleanup here; complete cleanup in END block
|
||||||
|
$session->config->delete('cdn');
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
#
|
#
|
||||||
# escape and unescape
|
# escape and unescape
|
||||||
|
|
@ -468,4 +500,9 @@ END { ##Always clean-up
|
||||||
else {
|
else {
|
||||||
$session->config->delete('webServerPort');
|
$session->config->delete('webServerPort');
|
||||||
}
|
}
|
||||||
|
if ($savecdn) {
|
||||||
|
$session->config->set('cdn', $savecdn);
|
||||||
|
} else {
|
||||||
|
$session->config->delete('cdn');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
129
t/Storage.t
129
t/Storage.t
|
|
@ -29,7 +29,7 @@ my $cwd = Cwd::cwd();
|
||||||
|
|
||||||
my ($extensionTests, $fileIconTests) = setupDataDrivenTests($session);
|
my ($extensionTests, $fileIconTests) = setupDataDrivenTests($session);
|
||||||
|
|
||||||
my $numTests = 74; # increment this value for each test you create
|
my $numTests = 102; # increment this value for each test you create
|
||||||
plan tests => $numTests + scalar @{ $extensionTests } + scalar @{ $fileIconTests };
|
plan tests => $numTests + scalar @{ $extensionTests } + scalar @{ $fileIconTests };
|
||||||
|
|
||||||
my $uploadDir = $session->config->get('uploadsPath');
|
my $uploadDir = $session->config->get('uploadsPath');
|
||||||
|
|
@ -73,6 +73,12 @@ is( $storage1->getPathFrag, '7e/8a/7e8a1b6a', 'pathFrag returns correct value');
|
||||||
#
|
#
|
||||||
####################################################
|
####################################################
|
||||||
|
|
||||||
|
my $savecdn = $session->config->get('cdn');
|
||||||
|
if ($savecdn) {
|
||||||
|
$session->config->delete('cdn');
|
||||||
|
}
|
||||||
|
# Note: the CDN configuration will be reverted after CDN tests below
|
||||||
|
|
||||||
my $storageDir1 = join '/', $uploadDir, '7e', '8a', '7e8a1b6a';
|
my $storageDir1 = join '/', $uploadDir, '7e', '8a', '7e8a1b6a';
|
||||||
is ($storage1->getPath, $storageDir1, 'getPath: path calculated correctly for directory');
|
is ($storage1->getPath, $storageDir1, 'getPath: path calculated correctly for directory');
|
||||||
my $storageFile1 = join '/', $storageDir1, 'baz';
|
my $storageFile1 = join '/', $storageDir1, 'baz';
|
||||||
|
|
@ -369,6 +375,118 @@ foreach my $iconTest (@{ $fileIconTests }) {
|
||||||
is( $storage1->getFileIconUrl($iconTest->{filename}), $iconTest->{iconUrl}, $iconTest->{comment} );
|
is( $storage1->getFileIconUrl($iconTest->{filename}), $iconTest->{iconUrl}, $iconTest->{comment} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
####################################################
|
||||||
|
#
|
||||||
|
# CDN (Content Delivery Network)
|
||||||
|
#
|
||||||
|
####################################################
|
||||||
|
|
||||||
|
my $cdnCfg = { "enabled" => 1,
|
||||||
|
"url" => "file:///data/storage",
|
||||||
|
"queuePath" => "/data/cdnqueue",
|
||||||
|
"syncProgram" => "cp -r -- '%s' /data/storage/",
|
||||||
|
"deleteProgram" => "rm -r -- '/data/storage/%s' > /dev/null 2>&1"
|
||||||
|
};
|
||||||
|
my ($addedCdnQ, $addedCdnU);
|
||||||
|
$addedCdnQ = mkdir $cdnCfg->{'queuePath'} unless -e $cdnCfg->{'queuePath'};
|
||||||
|
my $dest = substr($cdnCfg->{'url'}, 7);
|
||||||
|
$addedCdnU = mkdir $dest unless -e $dest;
|
||||||
|
$session->config->set('cdn', $cdnCfg);
|
||||||
|
my $cdnUrl = $cdnCfg->{'url'};
|
||||||
|
my $cdnUlen = length $cdnUrl;
|
||||||
|
my $cdnStorage = WebGUI::Storage->create($session);
|
||||||
|
# Functional URL before sync done
|
||||||
|
my $hexId = $session->id->toHex($cdnStorage->getId);
|
||||||
|
my $initUrl = join '/', $uploadUrl, $cdnStorage->getPathFrag;
|
||||||
|
is ($cdnStorage->getUrl, $initUrl, 'CDN: getUrl: URL before sync');
|
||||||
|
$filename = $cdnStorage->addFileFromScalar('cdnfile1', $content);
|
||||||
|
is ($filename, 'cdnfile1', 'CDN: filename returned by addFileFromScalar');
|
||||||
|
my $qFile = $cdnCfg->{'queuePath'} . '/' . $session->id->toHex($cdnStorage->getId);
|
||||||
|
my $dotCdn = $cdnStorage->getPath . '/.cdn';
|
||||||
|
ok (-e $qFile, 'CDN: queue file created when file added to storage');
|
||||||
|
|
||||||
|
### getCdnFileIterator
|
||||||
|
my $found = 0;
|
||||||
|
my $sobj = undef;
|
||||||
|
my $flist;
|
||||||
|
my $cdnPath = substr($cdnUrl, 7) . '/' . $hexId;
|
||||||
|
my $cdnFn = $cdnPath . '/' . $filename;
|
||||||
|
my $locIter = WebGUI::Storage->getCdnFileIterator($session);
|
||||||
|
my $already; # test the object type only once
|
||||||
|
if (is(ref($locIter), 'CODE', 'CDN: getCdnFileIterator to return sub ref')) {
|
||||||
|
while (my $sobj = $locIter->()) {
|
||||||
|
unless ($already) {
|
||||||
|
ok($sobj->isa('WebGUI::Storage'), 'CDN: iterator produces Storage objects');
|
||||||
|
$already = 1;
|
||||||
|
}
|
||||||
|
if ($sobj->getId eq $cdnStorage->getId) { # the one we want to test with
|
||||||
|
++$found;
|
||||||
|
$flist = $sobj->getFiles;
|
||||||
|
if (is(scalar @$flist, 1, 'CDN: there is one file in the storage')) {
|
||||||
|
my $file1 = $flist->[0];
|
||||||
|
is ($file1, $filename, 'CDN: correct filename in the storage');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is ($found, 1, 'CDN: getCdnFileIterator found storage');
|
||||||
|
# TODO: I'm not sure how to make these run on MS-Windows.
|
||||||
|
# Should we SKIP in the meantime? ($^O eq 'MSWin32')
|
||||||
|
### syncToCdn
|
||||||
|
$cdnStorage->syncToCdn;
|
||||||
|
ok( (-e $cdnPath and -d $cdnPath), 'CDN: target directory created');
|
||||||
|
ok( (-e $cdnFn and -T $cdnFn), 'CDN: target text file created');
|
||||||
|
is (-s $cdnFn, length $content, 'CDN: file is the right size');
|
||||||
|
ok (!(-e $qFile), 'CDN: queue file removed after sync');
|
||||||
|
ok (-e $dotCdn, 'CDN: dot-cdn flag file present after sync');
|
||||||
|
### getUrl with CDN
|
||||||
|
my $locUrl = $cdnUrl . '/' . $session->id->toHex($cdnStorage->getId);
|
||||||
|
is ($cdnStorage->getUrl, $locUrl, 'CDN: getUrl: URL for directory');
|
||||||
|
my $fileUrl = $locUrl . '/' . 'cdn-file';
|
||||||
|
is ($cdnStorage->getUrl('cdn-file'), $fileUrl, 'CDN: getUrl: URL for file');
|
||||||
|
# SSL
|
||||||
|
my %mockEnv = %ENV;
|
||||||
|
my $env = Test::MockObject::Extends->new($session->env);
|
||||||
|
$env->mock('get', sub { return $mockEnv{$_[1]} } );
|
||||||
|
$mockEnv{HTTPS} = 'on';
|
||||||
|
$cdnCfg->{'sslAlt'} = 1;
|
||||||
|
$session->config->set('cdn', $cdnCfg);
|
||||||
|
is ($cdnStorage->getUrl, $initUrl, 'CDN: getUrl: URL with sslAlt flag');
|
||||||
|
$cdnCfg->{'sslUrl'} = 'https://ssl.example.com';
|
||||||
|
$session->config->set('cdn', $cdnCfg);
|
||||||
|
my $sslUrl = $cdnCfg->{'sslUrl'} . '/' . $session->id->toHex($cdnStorage->getId);
|
||||||
|
is ($cdnStorage->getUrl, $sslUrl, 'CDN: getUrl: sslUrl');
|
||||||
|
$mockEnv{HTTPS} = undef;
|
||||||
|
is ($cdnStorage->getUrl, $locUrl, 'CDN: getUrl: cleartext request to not use sslUrl');
|
||||||
|
# Copy
|
||||||
|
my $cdnCopy = $cdnStorage->copy;
|
||||||
|
my $qcp = $cdnCfg->{'queuePath'} . '/' . $session->id->toHex($cdnCopy->getId);
|
||||||
|
ok (-e $qcp, 'CDN: queue file created when storage location copied');
|
||||||
|
my $dotcp = $cdnCopy->getPath . '/.cdn';
|
||||||
|
ok (!(-e $dotcp), 'CDN: dot-cdn flag file absent after copy');
|
||||||
|
# On clear, need to see the entry in cdnQueue
|
||||||
|
$qFile = $cdnCfg->{'queuePath'} . '/' . $session->id->toHex($cdnStorage->getId);
|
||||||
|
$cdnStorage->clear;
|
||||||
|
ok (-e $qFile, 'CDN: queue file created when storage cleared');
|
||||||
|
ok (-s $qFile >= 7 && -s $qFile <= 9, 'CDN: queue file has right size for deleted (clear)');
|
||||||
|
ok (!(-e $dotCdn), 'CDN: dot-cdn flag file absent after clear');
|
||||||
|
### deleteFromCdn
|
||||||
|
$cdnStorage->deleteFromCdn;
|
||||||
|
ok(! (-e $cdnPath), 'CDN: target directory removed');
|
||||||
|
ok(! (-e $qFile), 'CDN: queue file removed');
|
||||||
|
# Idea: add a file back before testing delete
|
||||||
|
# Note: expect it is necessary to be able to delete after clear.
|
||||||
|
# On delete, need to see the entry in cdnQueue
|
||||||
|
$cdnStorage->delete;
|
||||||
|
ok (-e $qFile, 'CDN: queue file created when storage deleted');
|
||||||
|
ok (-s $qFile >= 7 && -s $qFile <= 9, 'CDN: queue file has right size for deleted');
|
||||||
|
$cdnStorage->deleteFromCdn;
|
||||||
|
ok(! (-e $qFile), 'CDN: queue file removed');
|
||||||
|
|
||||||
|
# partial cleanup here; complete cleanup in END block
|
||||||
|
undef $cdnStorage;
|
||||||
|
$session->config->delete('cdn');
|
||||||
|
|
||||||
|
|
||||||
####################################################
|
####################################################
|
||||||
#
|
#
|
||||||
|
|
@ -452,8 +570,15 @@ END {
|
||||||
$storage1, $storage2, $storage3, $copiedStorage,
|
$storage1, $storage2, $storage3, $copiedStorage,
|
||||||
$secondCopy, $s3copy, $tempStor, $tarStorage,
|
$secondCopy, $s3copy, $tempStor, $tarStorage,
|
||||||
$untarStorage, $fileStore,
|
$untarStorage, $fileStore,
|
||||||
$hackedStore,
|
$hackedStore, $cdnStorage, $cdnCopy,
|
||||||
) {
|
) {
|
||||||
ref $stor eq "WebGUI::Storage" and $stor->delete;
|
ref $stor eq "WebGUI::Storage" and $stor->delete;
|
||||||
}
|
}
|
||||||
|
if ($savecdn) {
|
||||||
|
$session->config->set('cdn', $savecdn);
|
||||||
|
} else {
|
||||||
|
$session->config->delete('cdn');
|
||||||
|
}
|
||||||
|
$addedCdnQ and rmdir $addedCdnQ;
|
||||||
|
$addedCdnU and rmdir $addedCdnU;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue