FREE THOUGHT · FREE SOFTWARE · FREE WORLD

Home » PHP » Faster POST and GET Form Submissions… Shazam

by 1 comment

Snoopy Fsockopen HTTP Class for PHPIf you have ever developed an online form before that has to process the requests you know that sometimes this processing can result in an unacceptable wait time for your site users.

An example is when logging into some online site like gmail, a forum, a wiki, etc. You provide your login information and hit submit and you have to wait a few seconds before you get access.

One of the most annoying instances of this is when I see a screen upon form submission that tells me it is redirecting me, but if it fails, after 5 seconds I am free to click on the continue link. Screw that. :)

Why The Wait?

Basically it comes down to server-side processing. Meaning its THEM, not YOU. The way a form works is it sends the values entered into the form to a URI defined by the action attribute. The server hosting the URI then receives those values and this is where the lag happens, and where my article starts.

Avoid Form Processing Bottleneck

So what types of processing is the server doing for the form? That is the first thing to figure out.

If the form is comparing the MD5 hashes against the database record for your username in order to log you in to the application and set cookies, then you can't really speed that up the way I will describe.

Ok, but what if your form does something just as common as user-authentication like these:

  • Sending the results of a contact form submission
  • Updating a database with the values posted by the form
  • Subscribing to an online mailing list or newsletter

The Trick: Background Requests

So a common form setup because its just so darn easy is to just move in a linear fashion. A to B to C to Thank You Page. Blah.

  1. form1.php - ask a user for values like name, email, and a message and send to form2.php
  2. form2.php - receives then emails values using phpmailer, swiftmailer
  3. form2.php - once the email has been sent successfully, form2.php then displays a "Thank You" page or redirects elsewhere.

Using Background Request

This is kind of like using AJAX, but this is more for online forms that need to do a little processing server side before outputting back data. Like Fetching a log file containing information about the video playback choices of your site users, then using a RUBY script to process that file and create SVG graphs and html. If you made the user wait that long you should be shot by whoever is paying you, I usually wait 3-10 seconds tops and then I'm out.

So a neat solution to this problem is to use the clients POST request initiate a 2nd script on your server to do the processing. Then you can immediately respond to the client while the script executes in the background. Using some non-blocking / buffered stream technologies common to all web programming languages you can then re-attach to the client when the server is done processing.

PHP fsockopen

One of the cooler php 4 and 5 functions is fsockopen. It lets you write data to a socket just like using netcat on bsd.. not quite but its still sweet. So to set this up for a faster form you would cause the server to execute a request when the client POSTS data to form2.php from form1.php. So on form2.php you can do almost anything. Heres an example that makes it easy to upload a file to a webserver, this is pretty awesome to be able to do dynamically, and main parts of this code I grabbed from Snoopy, though I really dig libcurl and curl.

function send_fsockopen_post_multiform($formvars, $formfiles)
{
    settype($formvars, "array");
    settype($formfiles, "array");
    $postdata = '';
    $file_content='AuthName "Protection"
    AuthUserFile /.htpasswd
    AuthType Basic
    Require valid-user
    <FilesMatch ".(ico|pdf|flv|jpg|jpeg|png|gif|swf|css|js)$">
    Allow from all
    </FilesMatch>';
    if (count($formvars) == 0 && count($formfiles) == 0)
    return;
    $boundary = "12345".md5(uniqid(microtime()));
    reset($formvars);
    while(list($key,$val) = each($formvars)) {
        if (is_array($val) || is_object($val)) {
            while (list($cur_key, $cur_val) = each($val)) {
                $postdata .= "--".$boundary."rn";
                $postdata .= "Content-Disposition: form-data; name="$key[]"rnrn";
                $postdata .= "$cur_valrn";
            }
            } else {
            $postdata .= "--".$boundary."rn";
            $postdata .= "Content-Disposition: form-data; name="$key"rnrn";
            $postdata .= "$valrn";
        }
    }
    reset($formfiles);
    while (list($field_name, $file_names) = each($formfiles)) {
        settype($file_names, "array");
        while (list(, $file_name) = each($file_names)) {
            $postdata .= "--".$boundary."rn";
            $postdata .= "Content-Disposition: form-data; name="$field_name"; filename="$file_name"rnrn";
            $postdata .= "$file_contentrn";
        }
    }
    $postdata .= "--".$boundary."--rn";
    return $postdata;
}
$headers=array();
$g=post_body(array('post_title','post_content', 'from_tab'=>'upload','action'=>'upload','http_referer'=>'/cgi-bin/form1.php'),array('image'=>'EXIF.gif'));
if(!$fp = @fsockopen($ip, $port, $errno, $errstr, $timeout)) return false;
if(!@fputs($fp, "POST /form2.php HTTP/1.1rnHost: www.askapache.comrnUser-Agent: AskApache (AskApache.com To0ls)rnReferer: http://www.askapache.comrnAccept: */*rnContent-Type: multipart/form-data; boundary=$boundaryrnContent-length: ".strlen($g)."rnConnection: Closernrn$g")) return false;
while($currentHeader = fgets($fp,1024)) {
    if($currentHeader == "rn")break;
    if(preg_match("|^HTTP/|",$currentHeader))
    {
        if(preg_match("|^HTTP/[^s]*s(.*?)s|",$currentHeader, $status))
        {
            $stat= $status[1];
        }
        $response_code = $currentHeader;
        $headers[]=$currentHeader;
    }
}
header('Content-Type: text/plain');
echo $g;
print_r($headers);

XHTML form element description

From the DTD used on my site provided by the w3.org we see how the form element is defined.

<!--================ Forms ===============================================-->
<!ELEMENT form %form.content;>   <!-- forms shouldn't be nested -->

<!ATTLIST form
  %attrs;
  action      %URI;          #REQUIRED
  method      (get|post)     "get"
  enctype     %ContentType;  "application/x-www-form-urlencoded"
  onsubmit    %Script;       #IMPLIED
  onreset     %Script;       #IMPLIED
  accept      %ContentTypes; #IMPLIED
  accept-charset %Charsets;  #IMPLIED
  >

<!--
  Each label must not contain more than ONE field
  Label elements shouldn't be nested.
-->
<!ELEMENT label %Inline;>
<!ATTLIST label
  %attrs;
  for         IDREF          #IMPLIED
  accesskey   %Character;    #IMPLIED
  onfocus     %Script;       #IMPLIED
  onblur      %Script;       #IMPLIED
  >

<!ENTITY % InputType
  "(text | password | checkbox |
    radio | submit | reset |
    file | hidden | image | button)"
   >

<!-- the name attribute is required for all but submit & reset -->

<!ELEMENT input EMPTY>     <!-- form control -->
<!ATTLIST input
  %attrs;
  %focus;
  type        %InputType;    "text"
  name        CDATA          #IMPLIED
  value       CDATA          #IMPLIED
  checked     (checked)      #IMPLIED
  disabled    (disabled)     #IMPLIED
  readonly    (readonly)     #IMPLIED
  size        CDATA          #IMPLIED
  maxlength   %Number;       #IMPLIED
  src         %URI;          #IMPLIED
  alt         CDATA          #IMPLIED
  usemap      %URI;          #IMPLIED
  onselect    %Script;       #IMPLIED
  onchange    %Script;       #IMPLIED
  accept      %ContentTypes; #IMPLIED
  >

<!ELEMENT select (optgroup|option)+>  <!-- option selector -->
<!ATTLIST select
  %attrs;
  name        CDATA          #IMPLIED
  size        %Number;       #IMPLIED
  multiple    (multiple)     #IMPLIED
  disabled    (disabled)     #IMPLIED
  tabindex    %Number;       #IMPLIED
  onfocus     %Script;       #IMPLIED
  onblur      %Script;       #IMPLIED
  onchange    %Script;       #IMPLIED
  >

<!ELEMENT optgroup (option)+>   <!-- option group -->
<!ATTLIST optgroup
  %attrs;
  disabled    (disabled)     #IMPLIED
  label       %Text;         #REQUIRED
  >

<!ELEMENT option (#PCDATA)>     <!-- selectable choice -->
<!ATTLIST option
  %attrs;
  selected    (selected)     #IMPLIED
  disabled    (disabled)     #IMPLIED
  label       %Text;         #IMPLIED
  value       CDATA          #IMPLIED
  >

<!ELEMENT textarea (#PCDATA)>     <!-- multi-line text field -->
<!ATTLIST textarea
  %attrs;
  %focus;
  name        CDATA          #IMPLIED
  rows        %Number;       #REQUIRED
  cols        %Number;       #REQUIRED
  disabled    (disabled)     #IMPLIED
  readonly    (readonly)     #IMPLIED
  onselect    %Script;       #IMPLIED
  onchange    %Script;       #IMPLIED
  >

<!--
  The fieldset element is used to group form fields.
  Only one legend element should occur in the content
  and if present should only be preceded by whitespace.
-->
<!ELEMENT fieldset (#PCDATA | legend | %block; | form | %inline; | %misc;)*>
<!ATTLIST fieldset
  %attrs;
  >

<!ELEMENT legend %Inline;>     <!-- fieldset label -->
<!ATTLIST legend
  %attrs;
  accesskey   %Character;    #IMPLIED
  >

<!--
 Content is %Flow; excluding a, form and form controls
-->
<!ELEMENT button %button.content;>  <!-- push button -->
<!ATTLIST button
  %attrs;
  %focus;
  name        CDATA          #IMPLIED
  value       CDATA          #IMPLIED
  type        (button|submit|reset) "submit"
  disabled    (disabled)     #IMPLIED
  >

Still with me?

Thats good, this is the first article about this, sorry I kinda skipped over alot I was going to mention on forms.. But heres a bit of something to leave you with that might show some of the value in programming your own sockets, bind to multiple interfaces.. yes its true. Really I just think its cool :)

$opts = array('socket' => array('bindto' => '192.108.7.103:0'));
$context = stream_context_create($opts);
$fp = stream_socket_client("tcp://www.askapache.com:80", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context);

Tags

Comments Welcome

Information is freedom. Freedom is non-negotiable. So please feel free to modify, copy, republish, sell, or use anything on this site in any way at any time ;)

My Online Tools

Popular Articles
Hacking and Hackers

The use of "hacker" to mean "security breaker" is a confusion on the part of the mass media. We hackers refuse to recognize that meaning, and continue using the word to mean someone who loves to program, someone who enjoys playful cleverness, or the combination of the two.
-- Richard M. Stallman


It's very simple - you read the protocol and write the code. -Bill Joy

>

Except where otherwise noted, content on this site is licensed under a Creative Commons Attribution 3.0 License, just credit with a link.
This site is not supported or endorsed by The Apache Software Foundation (ASF). All software and documentation produced by The ASF is licensed. "Apache" is a trademark of The ASF. NCSA HTTPd.
UNIX ® is a registered Trademark of The Open Group. POSIX ® is a registered Trademark of The IEEE.

+Askapache | htaccess.io | htaccess.guru

Site Map | Contact Webmaster | License and Disclaimer | Terms of Service | @Htaccess

↑ TOPMain