Anti-DDoS enhancements: Log to net log, UMA stats, improved policy.

Count only certain error codes as failures. Don't count malformed body
for codes already counted failures. Start with fresh entry when
outdated, which makes policy slightly less aggressive.

Add registration/unregistration to NetworkChangeNotifier, and reset
throttling state on network changes, to avoid degenerate corner cases.

Add UMA stats for throttling, and unit tests for them.

Add hash-location-only URL to unit test.

Enable testing of AJAX behavior via backoff_server.

BUG=83775
TEST=net_unittests

Review URL: http://codereview.chromium.org/6966038

git-svn-id: http://src.chromium.org/svn/trunk/src/net/tools/testserver@87056 4ff67af0-8c30-449e-8e8b-ad334ec8d88c
diff --git a/backoff_server.py b/backoff_server.py
index 4188cd1..783fd83 100644
--- a/backoff_server.py
+++ b/backoff_server.py
@@ -13,8 +13,50 @@
 import urlparse

 

 

+AJAX_TEST_PAGE = '''

+<html>

+<head>

+<script>

+

+function reportResult(txt) {

+  var element = document.createElement('p');

+  element.innerHTML = txt;

+  document.body.appendChild(element);

+}

+

+function fetch() {

+  var response_code = document.getElementById('response_code');

+

+  xmlhttp = new XMLHttpRequest();

+  xmlhttp.open("GET",

+               "http://%s:%d/%s?code=" + response_code.value,

+               true);

+  xmlhttp.onreadystatechange = function() {

+    reportResult(

+        'readyState=' + xmlhttp.readyState + ', status=' + xmlhttp.status);

+  }

+  try {

+    xmlhttp.send(null);

+  } catch (e) {

+    reportResult('Exception: ' + e);

+  }

+}

+

+</script>

+</head>

+<body>

+<form action="javascript:fetch()">

+  Response code to get: <input id="response_code" type="text" value="503">

+  <input type="submit">

+</form>

+</body>

+</html>'''

+

+

 class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):

   keep_running = True

+  local_ip = ''

+  port = 0

 

   def do_GET(self):

     if self.path == '/quitquitquit':

@@ -25,22 +67,43 @@
       RequestHandler.keep_running = False

       return

 

+    if self.path.startswith('/ajax/'):

+      self.send_response(200)

+      self.send_header('Content-Type', 'text/html')

+      self.end_headers()

+      self.wfile.write(AJAX_TEST_PAGE % (self.local_ip,

+                                         self.port,

+                                         self.path[6:]))

+      return

+

     params = urlparse.parse_qs(urlparse.urlparse(self.path).query)

 

+    def SendCustomRetryIfRequested():

+      if params and 'custom-retry-after' in params:

+        custom_retry = params['custom-retry-after'][0]

+        self.send_header('X-Retry-After', custom_retry)

+

     if not params or not 'code' in params or params['code'][0] == '200':

       self.send_response(200)

       self.send_header('Content-Type', 'text/plain')

+      SendCustomRetryIfRequested()

       self.end_headers()

       self.wfile.write('OK')

     else:

-      self.send_error(int(params['code'][0]))

+      status_code = int(params['code'][0])

+      self.send_response(status_code)

+      SendCustomRetryIfRequested()

+      self.end_headers()

+      self.wfile.write('Error %d' % int(status_code))

 

 

 def main():

-  if len(sys.argv) != 2:

-    print "Usage: %s PORT" % sys.argv[0]

+  if len(sys.argv) != 3:

+    print "Usage: %s LOCAL_IP PORT" % sys.argv[0]

     sys.exit(1)

-  port = int(sys.argv[1])

+  RequestHandler.local_ip = sys.argv[1]

+  port = int(sys.argv[2])

+  RequestHandler.port = port

   print "To stop the server, go to http://localhost:%d/quitquitquit" % port

   httpd = BaseHTTPServer.HTTPServer(('', port), RequestHandler)

   while RequestHandler.keep_running: