Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ajax GET Request is Sent Twice

I have encountered baffling behavior when running an Ajax request as part of my Flask application. I have written a handler receive a div click and then send an Ajax request with certain data to a specific route specified in my app.py. The data is then inserted into a database. While this approach worked fine when running my Flask app on my own machine, upon moving my app to another hosting service (Pythonanywhere), every time I click the div, the request is being sent twice, as evidenced by the data being inserted twice into the database.

Similar variants of this question have been asked before (here and here, for instance), but those questions all deal with POST requests, while mine is using a GET. Additionally, those questions generally involved an HTML form that was being submitted alongside the POST request, hence the additional request. However, my code does not have any forms.

My code sample (simplified, but the same in essence to my current efforts):

In frontend.html:

<div class='wrapper'>
   <div class='submit_stamp' data-timestamp='2019-8-2'>Submit</div>
</div>

In frontend.js:

$('.wrapper').on('click', '.submit_stamp', function(){
   $.ajax({
     url: "/submit_time",
     type: "get",
     data: {time: $(this).data('timestamp')},
     success: function(response) {
       $('.wrapper').append(response.html);
     },

   });
});

In app.py:

@app.route('/submit_time')
def submit_time():
   db_manager.submit_stamp(flask.request.args.get('time'))
   return flask.jsonify({'html':'<p>Added timestamp</p>'})

As such, whenever I click the submit_stamp element, the Ajax request fires twice, the timestamp is inserted twice into my database, and "Added timestamp" is appended twice to .wrapper. Some things I have done to fix this include:

  1. Adding an event.stopPropagation() in the handler

  2. Using a boolean flag system where a variable is set to true just after the click, and reset to false in the success handler of the .ajax. I wrapped the $.ajax with this boolean in a conditional statement.

None of these patches worked. What confuses me, however, is why $.ajax is called once when running on my machine, but is called twice when running on the hosting service. Does it have to do with the cache? How can I resolve this issue? Thank you very much!

Edit:

Strangely, the duplicate requests occur infrequently. Sometimes, only one request is made, other times, the requests are duplicated. However, I have checked Network XHR output in Chrome and it is only displaying the single request header.

The access log output (with IPs removed):

<IP1> - - [05/Aug/2019:16:35:03 +0000] "GET /submit_time?payload=.... HTTP/1.1" 200 76 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1" "<IP>" response-time=0.217
<IP2> - - [05/Aug/2019:16:35:05 +0000] "GET /submit_time?payload=.... HTTP/1.1" 200 71 "http://www.katchup.work/create" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36" "<IP2>" response-time=0.198
like image 588
Ajax1234 Avatar asked Aug 03 '19 01:08

Ajax1234


2 Answers

Thank you to everyone who responded. Ultimately, I was able to resolve this issue with two different solutions:

1) First, I was able to block the offending request by checking the IP in the backend:

@app.route('/submit_time')
def submit_time():
   _ip = flask.request.environ.get('HTTP_X_REAL_IP', flask.request.remote_addr)
   if _ip == '128.177.108.218':
     return flask.jsonify({'route':'UNDEFINED-RESULT'.lower()})
   return flask.jsonify({"html":'<p>Added timestamp</p>'})

The above is really more of a temporary hack, as there is no guarantee the target IP will stay the same.

2) However, I discovered that running on HTTPS also removed the duplicate request. Originally, I was loading my app from the Pythonanywhere dashboard, resulting in http://www.testsite.com. However, once I installed a proper SSL certificate, refreshed the page, and ran the request again, I found that the desired result was produced.

I am awarding the bounty to @computercarguy as his post prompted me to think about the external/network related reasons why my original attempt was failing.

like image 89
Ajax1234 Avatar answered Oct 08 '22 17:10

Ajax1234


With your latest update, I'd have to say this isn't a duplicate request. With your log saying one request was from Mozilla on a Windows based machine, and the other request coming from Chrome on a Mac, it's simply 2 different requests coming from two different locations that happen to be close to each other in time. Even if it was a test from a virtual machine, it shouldn't record the multiple OSs or browsers, as VM will take care of all translations, preventing confusion like this.

You don't include IP addresses, but if they are public addresses (as in something other than 127.x.x.x, 10.x.x.x, or 192.x.x.x) then they are definitely two different users that happen to be using your software at the same time.

If you are tracking that it's the same user, it might simply be them using your software on 2 different devices (such as a desktop vs mobile phone). If that's not allowed, then make sure their access reflects this. If it can be tracked through DNS to different geographical locations, you might have a compromised account to lock down until the real user can confirm their identity.

However you slice it, with the new data, I don't think it's actually your software, unless you can reproduce it through testing even somewhat reliably. Take time to consider that it might just Not be a bug, but something else. Software devs are conditioned to think everything is a bug and their fault, when it could be something benign or a malicious attack that might not have been previously considered.

Good luck and hopefully I gave you something to think about!

like image 42
computercarguy Avatar answered Oct 08 '22 18:10

computercarguy