From 55000f98bcb84525d6d3f790601dffe190863bae Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 2 Aug 2017 14:56:16 +0200 Subject: [PATCH 1/2] include non-empty body with 202 responses reply claims to be application/json, so make sure it has some content --- jupyterhub/apihandlers/users.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/jupyterhub/apihandlers/users.py b/jupyterhub/apihandlers/users.py index 24e24993..5744e9c5 100644 --- a/jupyterhub/apihandlers/users.py +++ b/jupyterhub/apihandlers/users.py @@ -156,9 +156,9 @@ class UserAPIHandler(APIHandler): yield gen.maybe_future(self.authenticator.delete_user(user)) # remove from registry del self.users[user] - + self.set_status(204) - + @admin_only def patch(self, name): user = self.find_user(name) @@ -194,6 +194,8 @@ class UserServerAPIHandler(APIHandler): options = self.get_json_body() yield self.spawn_single_user(user, options=options) status = 202 if user.spawner._spawn_pending else 201 + if status == 202: + self.write(json.dumps({"message": "Server is slow to start"})) self.set_status(status) @gen.coroutine @@ -211,6 +213,8 @@ class UserServerAPIHandler(APIHandler): raise web.HTTPError(400, "%s's server is not running" % name) yield self.stop_single_user(user) status = 202 if user.spawner._stop_pending else 204 + if status == 202: + self.write(json.dumps({"message": "Server is slow to stop"})) self.set_status(status) @@ -232,6 +236,8 @@ class UserNamedServerAPIHandler(APIHandler): yield self.spawn_single_user(user, server_name, options=options) spawner = user.spawners[server_name] status = 202 if spawner._spawn_pending else 201 + if status == 202: + self.write(json.dumps({"message": "Server is slow to start"})) self.set_status(status) @gen.coroutine @@ -256,6 +262,8 @@ class UserNamedServerAPIHandler(APIHandler): raise web.HTTPError(400, "%s's server %s is not running" % (name, server_name)) yield self.stop_single_user(user, server_name) status = 202 if spawner._stop_pending else 204 + if status == 202: + self.write(json.dumps({"message": "Server is slow to stop"})) self.set_status(status) From 57c8ad6b92f71e0c305dfef8316da70f20b81b50 Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 2 Aug 2017 15:05:26 +0200 Subject: [PATCH 2/2] consolidate server APIHandlers no need for separate handlers for named and default servers avoid claiming that empty responses are JSON. --- jupyterhub/apihandlers/users.py | 89 +++++++++------------------------ 1 file changed, 24 insertions(+), 65 deletions(-) diff --git a/jupyterhub/apihandlers/users.py b/jupyterhub/apihandlers/users.py index 5744e9c5..23b23598 100644 --- a/jupyterhub/apihandlers/users.py +++ b/jupyterhub/apihandlers/users.py @@ -174,96 +174,55 @@ class UserAPIHandler(APIHandler): setattr(user, key, value) self.db.commit() self.write(json.dumps(self.user_model(user))) - + class UserServerAPIHandler(APIHandler): - """Create and delete single-user servers - - This handler should be used when c.JupyterHub.allow_named_servers = False - """ + """Start and stop single-user servers""" @gen.coroutine @admin_or_self - def post(self, name): + def post(self, name, server_name=''): user = self.find_user(name) - if user.running: + if server_name: + if not self.allow_named_servers: + raise web.HTTPError(400, "Named servers are not enabled.") + spawner = user.spawners[server_name] + if spawner.ready: # include notify, so that a server that died is noticed immediately - state = yield user.spawner.poll_and_notify() + state = yield spawner.poll_and_notify() if state is None: - raise web.HTTPError(400, "%s's server is already running" % name) + raise web.HTTPError(400, "%s's server %s is already running" % (name, server_name)) - options = self.get_json_body() - yield self.spawn_single_user(user, options=options) - status = 202 if user.spawner._spawn_pending else 201 - if status == 202: - self.write(json.dumps({"message": "Server is slow to start"})) - self.set_status(status) - - @gen.coroutine - @admin_or_self - def delete(self, name): - user = self.find_user(name) - if user.spawner._stop_pending: - self.set_status(202) - return - if not user.running: - raise web.HTTPError(400, "%s's server is not running" % name) - # include notify, so that a server that died is noticed immediately - status = yield user.spawner.poll_and_notify() - if status is not None: - raise web.HTTPError(400, "%s's server is not running" % name) - yield self.stop_single_user(user) - status = 202 if user.spawner._stop_pending else 204 - if status == 202: - self.write(json.dumps({"message": "Server is slow to stop"})) - self.set_status(status) - - -class UserNamedServerAPIHandler(APIHandler): - """Manage named single-user servers - - This handler should be used when c.JupyterHub.allow_named_servers = True - """ - @gen.coroutine - @admin_or_self - def post(self, name, server_name): - if not self.allow_named_servers: - raise web.HTTPError(400, "Named servers are not enabled.") - user = self.find_user(name) - if user is None: - raise web.HTTPError(404, "No such user '%s'" % name) - options = self.get_json_body() yield self.spawn_single_user(user, server_name, options=options) - spawner = user.spawners[server_name] - status = 202 if spawner._spawn_pending else 201 - if status == 202: - self.write(json.dumps({"message": "Server is slow to start"})) + status = 202 if spawner.pending == 'spawn' else 201 + self.set_header('Content-Type', 'text/plain') self.set_status(status) @gen.coroutine @admin_or_self - def delete(self, name, server_name): - if not self.allow_named_servers: - raise web.HTTPError(400, "Named servers are not enabled.") + def delete(self, name, server_name=''): user = self.find_user(name) - if user is None: - raise web.HTTPError(404, "No such user '%s'" % name) - if server_name not in user.spawners: - raise web.HTTPError(404, "%s has no server named '%s'" % (name, server_name)) + if server_name: + if not self.allow_named_servers: + raise web.HTTPError(400, "Named servers are not enabled.") + if server_name not in user.spawners: + raise web.HTTPError(404, "%s has no server named '%s'" % (name, server_name)) + spawner = user.spawners[server_name] + if spawner._stop_pending: + self.set_header('Content-Type', 'text/plain') self.set_status(202) return if not spawner.ready: - raise web.HTTPError(400, "%s's server %s is not running" % (name, server_name)) + raise web.HTTPError(400, "%s's server %s is not running" % (name, server_name)) # include notify, so that a server that died is noticed immediately status = yield spawner.poll_and_notify() if status is not None: raise web.HTTPError(400, "%s's server %s is not running" % (name, server_name)) yield self.stop_single_user(user, server_name) status = 202 if spawner._stop_pending else 204 - if status == 202: - self.write(json.dumps({"message": "Server is slow to stop"})) + self.set_header('Content-Type', 'text/plain') self.set_status(status) @@ -292,6 +251,6 @@ default_handlers = [ (r"/api/users", UserListAPIHandler), (r"/api/users/([^/]+)", UserAPIHandler), (r"/api/users/([^/]+)/server", UserServerAPIHandler), - (r"/api/users/([^/]+)/servers/([^/]*)", UserNamedServerAPIHandler), + (r"/api/users/([^/]+)/servers/([^/]*)", UserServerAPIHandler), (r"/api/users/([^/]+)/admin-access", UserAdminAccessAPIHandler), ]