Adam
Adam

Reputation: 29119

Why is this form not submitted twice when hitting the button twice?

Edit 25.07.2018: As Pawnesh Kumar said in the answer, this seems to be a browser issue. If I hit the button multiple times in Firefox the below script will only send one POST request, but if I do the same in Chrome I get a POST request for each click.

However, I can replicate the problem in the video at 01:00. This means when I install Laravel with Authentication, then if I click the submit button in the login form twice, Firefox will send 2 requests.

Why is Firefox sometimes sending multiple POST request and sometimes only one, when clicking multiple times on the button?


I have a user table

id | name
 1 | John

where the field id is a primary, integer, auto-incremet key. When I submit a dummy form that only has one button, then this will insert a new record with name John. Now this is what I observed:

Why is that? I would expect that if I hit the submit button multiple times, then the form will send multiple requests - and inserts multiple rows.

Thats my form:

<form action="/test.php" method="POST">
  <input type="submit" value="Add">
</form>

which submits to test.php:

<?php
$servername = "localhost";
$username = "adam";
$password = "password";
$dbname = "test-db";

$conn = new mysqli($servername, $username, $password, $dbname);       
$sql = "INSERT INTO user (name) VALUES ('John')";
if ($conn->query($sql) === TRUE) {
    echo "New record created successfully";
}     
$conn->close();

sleep(4);

Because of the sleep part, I can click on the Add button multiple times in a row. However, no matter how often I click the Add button while loading, there is only one new row in the DB.

In my access.log file I also find only one GET and POST request after clicking the button twenty times:

2001:****:****:4400:****:****:****:**** - - [25/Jul/2018:11:30:03 +0200] "GET /test/form.php HTTP/1.1" 200 301 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0" "*******.net"

2001:****:****:4400:****:****:***:**** - - [25/Jul/2018:11:30:34 +0200] "POST /test/test.php HTTP/1.1" 200 31 "http://********.net/test/form.php" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0" "********.net"


Remark:

I have read about techniques to prevent multiple submissions either in the backend or frontend. Thus I think it should be possible to submit the form multiple times by hitting the button multiple times.

I also read in the wiki article Post/Redirect/Get that this pattern can not prevent if a user sumbits a form multiple times too quick:

If a web user refreshes before the initial submission has completed because of server lag, resulting in a duplicate POST request in certain user agents.

Also in this video at 1:00 someone double clicks on a button and gets an error because he submitted twice.

Upvotes: 1

Views: 4182

Answers (4)

Wololo
Wololo

Reputation: 861

Note: I'm no expert, so I might be wrong. This is just my opinion.

To me, this seems like a browser quirk. It seems like Firefox intentionally ignores all the extra requests.

Chrome

In case of Chrome, if you click on the add button multiple times, multiple requests are sent to the server. Initially, all the extra requests are canceled (you can learn more about canceled here) since the server is not responding. However, once the server responds (i-e, once the sleep time is up), all the requests are processed immediately. I don't know why, but the server only sleeps for the first request. The rest of the requests are processed immediately.

Chrome

Edge

Edge also sends multiple requests if you click the button multiple times.

Edge

Firefox

Firefox is a little weird. It just sends one request no matter how many times you click the button. All the extra requests are ignored.

Firefox

Upvotes: 4

Pawnesh Kumar
Pawnesh Kumar

Reputation: 494

It is a browser thing.

I believe you are talking about the situation when a user clicks multiple times on the button. While in the background the page start reloading.

When we hit the submit button browser send the request to server. Until the response receive some browser show the old(expired) page while some simple clean the view.

In Firefox, it won't fire any event in spite of clicking many times. Wherein Chrome it submit the request to the server every time user click.

So, the bottom line it is the way the browser handles things. Your logs also showing you were using Firefox so that the case.

Upvotes: 1

YanDatsiuk
YanDatsiuk

Reputation: 1963

  1. How do you know that your form was not submitted twice or more times?

The fact, that you do not have multiply rows in database doesn't proof that. Maybe column "name" is unique in your table?

  1. Check logs of your webserver. Example for apache: sudo cat /var/log/apache2/access.log

There you will find info of how many request you recieved.

Upvotes: 1

Sasa Blagojevic
Sasa Blagojevic

Reputation: 2200

It might be because you lock a file exclusively LOCK_EX with flock and then try to write to it with file_put_contents which under the hood opens a new pointer with fopen, and since you already locked the file exclusively it can't write to your file.

Try replacing the file_put_contents with fwrite($fp, $current, strlen($current)), and add an append flag to your fopen like this fopen($file, a+)

Does this reproduce the issue of multiple writes?

UPDATE

The token exception mismatch from the video you provided happens because of the Laravel's CSRF protection.

Every form in Laravel needs to have a {{ csrf_field() }} or you need to provide the crsf_token in the Header for AJAX requests.

That csrf_token in the form's hidden field has that same value stored in the Laravel's user session. When you double click submit fast on a form, the first request is sent with the first csrf_token, when it hits Laravel the token sent in the form request and the token stored in the session are compared, if they are equal the request passes, and a new csrf_token is generated in the session.

Since you did not reload the view the new token couldn't be rendered in the hidden form field, so by the time the second request hits we have a new csrf_token in the session and the old one in the second form request and thus the exception is thrown.

UPDATE 2

As I said it was the flock, try this code and the multiple submit issue will work

<?php

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
       $file = 'file.txt';
       $fp = fopen($file, 'a+');

       if (flock($fp, LOCK_EX)) {
           fwrite($fp, "+", 1);
           flock($fp, LOCK_UN);
       }
       sleep(2);
    }
 ?>
<!DOCTYPE html>
<html lang="en">
    <head>

    </head>
    <body>
        <form action="/" method="POST">
            <button type="submit">Submit</button>
        </form>
    </body>
</html>

Upvotes: 0

Related Questions