title: Strange behavior with multiple ajax requests to the same URL
body: <p>I constructed a strange situation in a CherryPy server, and I'd like some help understanding exactly what's happening, and why.  Here is <code>server.py</code>:</p>
  
  <pre class="lang-py prettyprint-override"><code>import cherrypy
  import os
  import threading
  
  class Root(object):
      def __init__(self):
          self.count = 0;
          self.lock = threading.Lock()
  
      @cherrypy.expose
      def app(self):
          with self.lock:
              self.count += 1
          return "Call #%d" % (self.count)
  
  if __name__ == '__main__':
      cherrypy.quickstart(Root(), "/", {"/": { "tools.staticdir.on": True,
                                               "tools.staticdir.dir": os.getcwd() } })
  </code></pre>
  
  <p>This is a very simple application with some state protected by a mutex.  Here is <code>index.html</code>:</p>
  
  <pre class="lang-xml prettyprint-override"><code>&lt;!doctype html&gt;
  &lt;script src=http://code.jquery.com/jquery-1.8.2.js&gt;&lt;/script&gt;
  &lt;script src=index.js&gt;&lt;/script&gt;
  </code></pre>
  
  <p>(It's ok for the HTML to be this sparse - see <a href="http://www.paulirish.com/2011/primitives-html5-video/" rel="nofollow">this talk from Paul Irish</a>)  Here is <code>index.js</code>:</p>
  
  <pre><code>function request() {
      var cb = function (response) {
          console.log(response);
      };
  
      $.ajax({
          url: "/app",
          success: cb
      });
  }
  
  function go_slow(N, delay) {
      if (N &gt; 0) {
          request();
          window.setTimeout(go_slow, delay, N - 1, delay);
      }
  }
  
  function go_fast(N) {
      var i;
      for (i = 0; i &lt; N; i++) {
          request();
      }
  }
  
  window.onload = function () {
      //go_slow(100, 0);
      go_fast(100);
  };
  </code></pre>
  
  <p>All of these files should go in the same directory.  When I start the CherryPy server and then visit <code>http://localhost:8080/index.html</code>, the console shows the phrase "Call #1" 100 times, and the CherryPy log shows one GET request to "/app".  In Firefox, I see several consecutive console messages, showing "#1" through "#100".  The Firefox behavior is what I expected, because I made 100 explicit ajax requests, and the function associated to these requests is supposed to return a different result each time.</p>
  
  <p>If instead of <code>go_fast()</code> I call <code>go_slow()</code> - a variant that uses "trampolining" (I think it's called) to force the ajax requests to be emitted from separate function calls at different times - then I get the "#1" through "#100" behavior on both Chrome and Firefox.</p>
  
  <p>Finally, if I modify the <code>app()</code> method in the CherryPy server to accept an argument which it then ignores, like this (note the <code>i</code> argument that doesn't appear anywhere else):</p>
  
  <pre><code>@cherrypy.expose
  def app(self, i):
      with self.lock:
          self.count += 1
      return "Call #%d" % (self.count)
  </code></pre>
  
  <p>and I arrange for the <code>go_fast()</code> function to make the ajax request to <code>"/app/" + i"</code> (where <code>i</code> is the loop index) instead of just <code>"/app"</code>, by changing <code>request()</code> and <code>go_fast()</code> like this:</p>
  
  <pre><code>function request(i) {
      var cb = function (response) {
          console.log(response);
      };
  
      $.ajax({
          url: "/app/" + i,
          success: cb
      });
  }
  
  function go_fast(N) {
      var i;
      for (i = 0; i &lt; N; i++) {
          request(i);
      }
  }
  </code></pre>
  
  <p>then I again get the "#1" through "#100" behavior.  If instead I modify the function to use, e.g., <code>"/app/7"</code> as the URL (i.e., replace <code>"/app/" + i</code> in the snippet above with <code>"/app/7"</code>), I get 100 repetitions of "Call #1" as in the initial example.</p>
  
  <p>To me it looks a lot like <em>something</em> is "caching" the result of the one and only ajax request that was actually made in the initial case (where I only saw "Call #1" 100 times).  The "Network" tab in Chrome's console just shows the one ajax request receiving a status code of "200 OK" and as far as I know, you have to explicitly enable server-side caching in CherryPy.  There is also the curiosity that Firefox gives the expected behavior in this case.</p>
  
  <p>In the other cases, for one reason or another, this "caching" mechanism is defeated.  Trampolining might cause the browser to toss out the immediate context of the ajax request, preventing it from "remembering" that a request to the same URL was made previously, while contriving the URL to require a positional argument simply gives each request a unique URL, forcing an actual request to go out, rather than using any caching mechanism.</p>
  
  <p>Can anyone explain concretely what's going on here?  Is this simply a peculiarity about about how Chrome handles many ajax requests in a short time frame?  Or is this situation a fatal problem with having stateful ajax resources?</p>