changeset 8:0efba0cf0ca3

added resource usage limits, /status endpoint
author Atul Varma <avarma@mozilla.com>
date Mon, 31 May 2010 12:18:28 -0700
parents f2fef5bb0396
children fb7ca2688d3d
files boxes/evil.js sjsbox/box.py sjsbox/server.py tests/test_box.py
diffstat 4 files changed, 49 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/boxes/evil.js	Mon May 31 12:18:28 2010 -0700
@@ -0,0 +1,6 @@
+function handle() {
+  var foo = [];
+  while (1) {
+    foo.push("i am nomming your memory forever!");
+  }
+}
--- a/sjsbox/box.py	Mon May 31 11:00:29 2010 -0700
+++ b/sjsbox/box.py	Mon May 31 12:18:28 2010 -0700
@@ -1,7 +1,17 @@
 import logging
+import resource
 import multiprocessing as mproc
 
 class BoxChild(object):
+    LIMITS = {
+        resource.RLIMIT_DATA: (1024 * 1024, 1024 * 2048),
+        resource.RLIMIT_STACK: (1024 * 512, 1024 * 1024),
+        resource.RLIMIT_RSS: (1024 * 1024, 1024 * 2048),
+        resource.RLIMIT_AS: (1024 * 1024, 1024 * 2048),
+        resource.RLIMIT_CPU: (5, 10),
+        resource.RLIMIT_NOFILE: (10, 20)
+        }
+
     def __init__(self, f, pipe):
         if f.name.endswith('.js'):
             import sjsbox.js
@@ -16,11 +26,21 @@
             if cmd == 'shutdown':
                 self.__impl.shutdown()
                 return
+            elif cmd == 'status':
+                rusage = resource.getrusage(resource.RUSAGE_SELF)
+                props = [name for name in dir(rusage)
+                         if name.startswith('ru_')]
+                retval = {}
+                for name in props:
+                    retval[name] = getattr(rusage, name)
+                self.pipe.send(retval)
             elif cmd == 'handle':
                 self.pipe.send(self.__impl.handle(*args))
 
     @classmethod
     def start(klass, *args, **kwargs):
+        for name, limits in klass.LIMITS.items():
+            resource.setrlimit(name, limits)
         obj = klass(*args, **kwargs)
         obj.run()
 
@@ -47,6 +67,11 @@
         self.child_pipe.send(('handle', (method, path)))
         return self.child_pipe.recv()
 
+    @property
+    def status(self):
+        self.child_pipe.send(('status', ()))
+        return self.child_pipe.recv()
+
     def shutdown(self):
         self.child_pipe.send(('shutdown', None))
         self.child.join(self.TIMEOUT)
--- a/sjsbox/server.py	Mon May 31 11:00:29 2010 -0700
+++ b/sjsbox/server.py	Mon May 31 12:18:28 2010 -0700
@@ -17,16 +17,23 @@
                            [('Content-Type', 'text/plain')])
             return ['Not Found: %s' % environ['PATH_INFO']]
 
+        def json_response(obj):
+            start_response('200 OK',
+                           [('Content-Type', self.JSON_TYPE)])
+            return [json.dumps(obj)]
+
         path_parts = environ['PATH_INFO'].split('/')[1:]
         if path_parts:
             boxname = path_parts[0]
-            if boxname in self.boxes:
+            if boxname == 'status':
+                if len(path_parts) != 2 or path_parts[1] not in self.boxes:
+                    return error_404()
+                return json_response(self.boxes[path_parts[1]].status)
+            elif boxname in self.boxes:
                 kwargs = dict(method=environ['REQUEST_METHOD'],
                               path='/%s' % '/'.join(path_parts[1:]))
                 retval = self.boxes[boxname].handle(**kwargs)
                 if retval == 404:
                     return error_404()
-                start_response('200 OK',
-                               [('Content-Type', self.JSON_TYPE)])
-                return [json.dumps(retval)]
+                return json_response(retval)
         return error_404()
--- a/tests/test_box.py	Mon May 31 11:00:29 2010 -0700
+++ b/tests/test_box.py	Mon May 31 12:18:28 2010 -0700
@@ -21,6 +21,13 @@
     def test_contains_works(self):
         self.assertTrue('foo' in self.boxes)
 
+    def test_handle_works(self):
+        self.assertEqual(self.boxes['foo'].handle('GET', '/'),
+                         404)
+
+    def test_status_works(self):
+        self.assertTrue('ru_maxrss' in self.boxes['foo'].status)
+
     def test_update_works(self):
         self.boxes.update()
         self.assertEqual(self.boxes['foo'].handle('GET', '/'),