Rogue has asked for the wisdom of the Perl Monks concerning the following question:

Hello,

Im currently attempting to implement a "Cancel" button for a cgi upload script in case the upload needs to be aborted. I've searched google for the past few days and can't find anything on how to stop a running cgi script.

I implemented a method I thought would work, but it doesn't seem to stop the CGI script. I am using the Extjs framework, so when I call my upload cgi script, the target is set to a hidden Iframe. I was able to use Extjs to grab the hidden iframe during the upload, call the stop method on its page, and remove the Iframe, yet the CGI script still manages to upload the whole file. I've verified with Firebug that the iframe is in fact being deleted when I hit the cancel button.

I looked for how to stop a form sumbit() call, but all I could find was form validation stuff, which I don't want. I also thought passing a 'cancel' flag to the running upload script would work, but I don't know how to pass anything to a RUNNING cgi script, if that's even possible. Finally, I thought I could have the cgi script periodically check for a cancel button push or something, but I couldn't find anything on that either.

Any help would be greatly appreciated.

Replies are listed 'Best First'.
Re: Cancelling a cgi file transfer
by Sewi (Friar) on Aug 24, 2009 at 21:17 UTC
    Do you already have some kind of sessioning set up?
    If you have:
  • Write the PID ($$) of the script processing the upload to the session
  • Point your Cancel-button to another CGI script which reads the PID of the upload-script from the session and kill's it
    kill 15,$Upload_PID; # Hard-kill and risk half-uploaded temp-files on +disk, if you write them kill 10,$Upload_PID; # Send SIGUSR1 to the upload-script
  • When using SIGUSR1 (second line above), you could clean up before aborting the upload script, for example:
    $SIG{USR1} = sub { unlink $Temp_File; exit; };
  • If you don't have any sessioning, add a hidden field with some unique token to the upload form. A lovely thing could be:

    my $Token = unpack('H*',pack('L',time).pack('S',$$));
    Your upload script makes itself findable using the token it received from the form (a good idea would be adding the token as GET parameter for early parsing), for example:
    $0 .= '('.$Token.')';
    or
    open my $ID_File,'>/dev/shm/tokenfiles/'.$Token; print $ID_File $$; close $ID_File;
    (You should verify the incoming token for matching /^\da-f$/ for security reasons for both solutions! Don't forget to clean up the file after the upload has finished when using a ID-file.)
    Feed the same token from the form to your cancel-script and let it find the PID of the upload process by searching the process table or looking at /dev/shm/tokenfiles/$Token. The rest is the same as above: Now as you know the PID, you could send a cancel signal to the process.
      Liked the main idea :) Perhaps the uploader can write some meta data of what it's doing to the session (instead of some %SIG trickery) and the killer can use that info to wipe-out any residues. However using a form field to identify something on the disk is not a wise thing and a huge security risk. Always use sessions. If you are not sure how to implement one, use one from the CPAN.
      I don't have any sort of sessioning set up, and I am running a WAMP server, so obtaining and killing process IDs isn't viable.

      I switched my server over to lighttpd running on cygwin and implemented your suggestion now that I have access to PIDs. I am just trying to get proof of concept at the moment, and I am running into a little problem. I fork my upload cgi script to create a text file with the PID of the upload script in a separate process.

      The cancel cgi script is reading the PID just fine, but the problem is that even though I am forking the upload process, that text file isn't showing up/being created for awhile after the "upload" button is pressed. I am waiting in the parent upload process and put a sleep on the child process(write text file with PID), but it still takes awhile for that text file to show up. Obviously if you click the Cancel button within a second or two of starting, it throws an error, because that file isnt there yet.

      Is this a problem that could be helped by using FastCGI?
        That last reply was me. I think this problem may be caused by the way lighttpd operates. It buffers everything before it sends(http://article.gmane.org/gmane.comp.web.lighttpd/3287), so I think the wait period I am experiencing before the file is created is the buffering.
Re: Cancelling a cgi file transfer
by Your Mother (Archbishop) on Aug 24, 2009 at 20:06 UTC

    IIRC this is only possible for Perl running under modperl. perrin will correct me if that's wrong. :) I Googled around a bit on this theme but didn't find what I remember reading 5 years ago.

    I doubt that is what you want though. For the general case, if it's really important to clear the file, I'd probably just add a method to your CGI which sends a follow-up "DELETE/POST" when you click "cancel" to remove the file that was just uploaded with the specified identifier/timestamp.