From 61edcc70d702c170a588fe7bd8a3f38ef0aed6eb Mon Sep 17 00:00:00 2001 From: Adam Wallner Date: Mon, 9 Feb 2015 18:28:30 +0100 Subject: [PATCH 1/8] - PEP 8 compliant - parameters to specify host and port to listen - signal handling to be able to stop if it is in the background - sleepyServer.sh start/stop script --- .gitignore | 1 + requirements.txt | 2 + sleepyServer.sh | 102 +++++++++++++++++ sleepymongoose/handlers.py | 208 +++++++++++++++++----------------- sleepymongoose/httpd.py | 173 ++++++++++++++++------------ t/get.py | 66 ----------- t/post.py | 226 ------------------------------------- test/get.py | 64 +++++++++++ test/post.py | 226 +++++++++++++++++++++++++++++++++++++ 9 files changed, 601 insertions(+), 467 deletions(-) create mode 100755 sleepyServer.sh delete mode 100644 t/get.py delete mode 100644 t/post.py create mode 100644 test/get.py create mode 100644 test/post.py diff --git a/.gitignore b/.gitignore index 518a1c6..d258d03 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *~ *.log +.idea \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index de4887b..a24ce42 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,3 @@ +bson +simplejson pymongo diff --git a/sleepyServer.sh b/sleepyServer.sh new file mode 100755 index 0000000..4170673 --- /dev/null +++ b/sleepyServer.sh @@ -0,0 +1,102 @@ +#!/bin/bash + +### Paramaters ### + +ADDRESS=localhost +PORT=27080 + +################## + + +# Action is the 1st parameter +action=$1 +shift + +python="`which python`" +dir="$(cd `dirname "$0"` && pwd)" +server="$dir/httpd.py" + + +# Process action +case ${action} in + # Start foreground mode with debug console + debug) + # Run it in foreground + "${python}" "${server}" $@ + ;; + + start) + # Check if running + pgrep -f "${server}" >/dev/null + if [ $? -eq 0 ]; then + echo "The server is already running."; + exit 1; + fi + + # Run it in background + "${python}" "${server}" $@ &>/dev/null & + + # Check if the server is running (max 10s to wait) + for i in 1 2 3 4 5; do + sleep 1 + wget -qO - ${ADDRESS}:${PORT}/_hello >/dev/null + res=$? + if [ ${res} -eq 0 ]; then + echo "The server has started successfully." + exit 0; + fi + done + killall ${server} + echo "The server has not started!" + exit 1; + ;; + + stop) + # Check if running + pgrep -f "${server}" >/dev/null + if [ $? -ne 0 ]; then + echo "The server is not running."; + exit 1; + fi + pkill -SIGINT -f "${server}" >/dev/null + + pgrep -f "${server}" >/dev/null + res=$? + + if [ ${res} -eq 0 ]; then + echo -n "Waiting for server to stop... " + for i in 5 4 3 2 1; do + echo -n -e "\b$i" + sleep 1 + # If it is still running + pgrep -f "${server}" >/dev/null + res=$? + if [ ${res} -ne 0 ]; then break; fi + done + + if [ ${res} -eq 0 ]; then + echo -e "\b Force kill server..." + pkill -f "${server}" >/dev/null + else + echo -e "\b\bOK" + fi + fi + + pgrep -f "${server}" >/dev/null + res=$? + + if [ ${res} -ne 0 ]; then + echo "The server stopped successfully." + else + echo "Some errors occured while stopping the server! Exit code: $res"; + exit ${res} + fi + ;; + test) + wget -qO - ${ADDRESS}:${PORT}/_hello + echo "" + ;; + *) + echo "Usage: `basename "$0"` start|stop|debug|test" + ;; +esac diff --git a/sleepymongoose/handlers.py b/sleepymongoose/handlers.py index 0e6a9b0..2daf59a 100644 --- a/sleepymongoose/handlers.py +++ b/sleepymongoose/handlers.py @@ -13,16 +13,18 @@ # limitations under the License. from bson.son import SON -from pymongo import Connection, ASCENDING, DESCENDING -from pymongo.errors import ConnectionFailure, ConfigurationError, OperationFailure, AutoReconnect -from bson import json_util +# from pymongo import Connection, ASCENDING, DESCENDING +# from pymongo.errors import ConnectionFailure, ConfigurationError, OperationFailure, AutoReconnect +# from bson import json_util import re + try: import json except ImportError: import simplejson as json + class MongoHandler: mh = None @@ -32,26 +34,26 @@ def __init__(self, mongos): self.connections = {} for host in mongos: - args = MongoFakeFieldStorage({"server" : host}) + args = MongoFakeFieldStorage({"server": host}) out = MongoFakeStream() if len(mongos) == 1: name = "default" else: - name = host.replace(".", "") + name = host.replace(".", "") name = name.replace(":", "") - self._connect(args, out.ostream, name = name) - - def _get_connection(self, name = None, uri='mongodb://localhost:27017'): - if name == None: + self._connect(args, out.ostream, name=name) + + def _get_connection(self, name=None, uri='mongodb://localhost:27017'): + if name is None: name = "default" if name in self.connections: return self.connections[name] - + try: - connection = Connection(uri, network_timeout = 2) + connection = Connection(uri, network_timeout=2) except (ConnectionFailure, ConfigurationError): return None @@ -63,62 +65,63 @@ def _get_host_and_port(self, server): port = 27017 if len(server) == 0: - return (host, port) + return host, port m = re.search('([^:]+):([0-9]+)?', server) - if m == None: - return (host, port) + if m is None: + return host, port handp = m.groups() if len(handp) >= 1: host = handp[0] - if len(handp) == 2 and handp[1] != None: + if len(handp) == 2 and handp[1] is not None: port = int(handp[1]) - return (host, port) + return host, port + # noinspection PyMethodParameters def sm_object_hook(obj): if "$pyhint" in obj: temp = SON() + # noinspection PyUnresolvedReferences for pair in obj['$pyhint']: temp[pair['key']] = pair['value'] return temp else: return json_util.object_hook(obj) - - def _get_son(self, str, out): + def _get_son(self, s, out): try: - obj = json.loads(str, object_hook=json_util.object_hook) + obj = json.loads(s, object_hook=json_util.object_hook) except (ValueError, TypeError): - out('{"ok" : 0, "errmsg" : "couldn\'t parse json: %s"}' % str) + out('{"ok" : 0, "errmsg" : "couldn\'t parse json: %s"}' % s) return None - if getattr(obj, '__iter__', False) == False: - out('{"ok" : 0, "errmsg" : "type is not iterable: %s"}' % str) + if not getattr(obj, '__iter__', False): + out('{"ok" : 0, "errmsg" : "type is not iterable: %s"}' % s) return None - - return obj + return obj - def _cmd(self, args, out, name = None, db = None, collection = None): - if name == None: + # noinspection PyUnusedLocal,PyUnresolvedReferences + def _cmd(self, args, out, name=None, db=None, collection=None): + if name is None: name = "default" conn = self._get_connection(name) - if conn == None: + if conn is None: out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') return cmd = self._get_son(args.getvalue('cmd'), out) - if cmd == None: + if cmd is None: return try: result = conn[db].command(cmd, check=False) except AutoReconnect: - out('{"ok" : 0, "errmsg" : "wasn\'t connected to the db and '+ + out('{"ok" : 0, "errmsg" : "wasn\'t connected to the db and ' + 'couldn\'t reconnect", "name" : "%s"}' % name) return except (OperationFailure, error): @@ -130,22 +133,25 @@ def _cmd(self, args, out, name = None, db = None, collection = None): result['cmd'] = args.getvalue('cmd') out(json.dumps(result, default=json_util.default)) - - def _hello(self, args, out, name = None, db = None, collection = None): - out('{"ok" : 1, "msg" : "Uh, we had a slight weapons malfunction, but ' + + + # noinspection PyUnusedLocal + def _hello(self, args, out, name=None, db=None, collection=None): + out('{"ok" : 1, "msg" : "Uh, we had a slight weapons malfunction, but ' + 'uh... everything\'s perfectly all right now. We\'re fine. We\'re ' + 'all fine here now, thank you. How are you?"}') return - - def _status(self, args, out, name = None, db = None, collection = None): - result = {"ok" : 1, "connections" : {}} + + # noinspection PyUnusedLocal + def _status(self, args, out, name=None, db=None, collection=None): + result = {"ok": 1, "connections": {}} for name, conn in self.connections.iteritems(): result['connections'][name] = "%s:%d" % (conn.host, conn.port) out(json.dumps(result)) - - def _connect(self, args, out, name = None, db = None, collection = None): + + # noinspection PyUnusedLocal + def _connect(self, args, out, name=None, db=None, collection=None): """ connect to a mongod """ @@ -158,23 +164,23 @@ def _connect(self, args, out, name = None, db = None, collection = None): try: uri = args.getvalue('server') except Exception, e: - print uri print e - out('{"ok" : 0, "errmsg" : "invalid server uri given", "server" : "%s"}' % uri) + out('{"ok" : 0, "errmsg" : "invalid server uri given"}') return else: uri = 'mongodb://localhost:27017' - if name == None: + if name is None: name = "default" conn = self._get_connection(name, uri) - if conn != None: + if conn is not None: out('{"ok" : 1, "server" : "%s", "name" : "%s"}' % (uri, name)) else: out('{"ok" : 0, "errmsg" : "could not connect", "server" : "%s", "name" : "%s"}' % (uri, name)) - def _authenticate(self, args, out, name = None, db = None, collection = None): + # noinspection PyUnusedLocal + def _authenticate(self, args, out, name=None, db=None, collection=None): """ authenticate to the database. """ @@ -184,26 +190,26 @@ def _authenticate(self, args, out, name = None, db = None, collection = None): return conn = self._get_connection(name) - if conn == None: + if conn is None: out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') return - if db == None: + if db is None: out('{"ok" : 0, "errmsg" : "db must be defined"}') return - if not 'username' in args: + if 'username' not in args: out('{"ok" : 0, "errmsg" : "username must be defined"}') - if not 'password' in args: + if 'password' not in args: out('{"ok" : 0, "errmsg" : "password must be defined"}') - + if not conn[db].authenticate(args.getvalue('username'), args.getvalue('password')): out('{"ok" : 0, "errmsg" : "authentication failed"}') else: out('{"ok" : 1}') - - def _find(self, args, out, name = None, db = None, collection = None): + + def _find(self, args, out, name=None, db=None, collection=None): """ query the database. """ @@ -213,24 +219,24 @@ def _find(self, args, out, name = None, db = None, collection = None): return conn = self._get_connection(name) - if conn == None: + if conn is None: out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') return - if db == None or collection == None: + if db is None or collection is None: out('{"ok" : 0, "errmsg" : "db and collection must be defined"}') - return + return criteria = {} if 'criteria' in args: criteria = self._get_son(args['criteria'][0], out) - if criteria == None: + if None == criteria: return fields = None if 'fields' in args: fields = self._get_son(args['fields'][0], out) - if fields == None: + if fields is None: return limit = 0 @@ -243,10 +249,9 @@ def _find(self, args, out, name = None, db = None, collection = None): cursor = conn[db][collection].find(spec=criteria, fields=fields, limit=limit, skip=skip) - sort = None if 'sort' in args: sort = self._get_son(args['sort'][0], out) - if sort == None: + if sort is None: return stupid_sort = [] @@ -260,27 +265,26 @@ def _find(self, args, out, name = None, db = None, collection = None): cursor.sort(stupid_sort) if 'explain' in args and bool(args['explain'][0]): - out(json.dumps({"results" : [cursor.explain()], "ok" : 1}, default=json_util.default)) - + out(json.dumps({"results": [cursor.explain()], "ok": 1}, default=json_util.default)) if not hasattr(self, "cursors"): setattr(self, "cursors", {}) - id = MongoHandler._cursor_id - MongoHandler._cursor_id = MongoHandler._cursor_id + 1 + _id = MongoHandler._cursor_id + MongoHandler._cursor_id += 1 cursors = getattr(self, "cursors") - cursors[id] = cursor - setattr(cursor, "id", id) + cursors[_id] = cursor + setattr(cursor, "id", _id) batch_size = 15 if 'batch_size' in args: batch_size = int(args['batch_size'][0]) - - self.__output_results(cursor, out, batch_size) + self.__output_results(cursor, out, batch_size) - def _more(self, args, out, name = None, db = None, collection = None): + # noinspection PyUnusedLocal + def _more(self, args, out, name=None, db=None, collection=None): """ Get more results from a cursor """ @@ -293,15 +297,14 @@ def _more(self, args, out, name = None, db = None, collection = None): out('{"ok" : 0, "errmsg" : "no cursor id given"}') return - - id = int(args["id"][0]) + _id = int(args["id"][0]) cursors = getattr(self, "cursors") - if id not in cursors: - out('{"ok" : 0, "errmsg" : "couldn\'t find the cursor with id %d"}' % id) + if _id not in cursors: + out('{"ok" : 0, "errmsg" : "couldn\'t find the cursor with id %d"}' % _id) return - cursor = cursors[id] + cursor = cursors[_id] batch_size = 15 if 'batch_size' in args: @@ -309,7 +312,6 @@ def _more(self, args, out, name = None, db = None, collection = None): self.__output_results(cursor, out, batch_size) - def __output_results(self, cursor, out, batch_size=15): """ Iterate through the next batch @@ -320,19 +322,18 @@ def __output_results(self, cursor, out, batch_size=15): while len(batch) < batch_size: batch.append(cursor.next()) except AutoReconnect: - out(json.dumps({"ok" : 0, "errmsg" : "auto reconnecting, please try again"})) + out(json.dumps({"ok": 0, "errmsg": "auto reconnecting, please try again"})) return except OperationFailure, of: - out(json.dumps({"ok" : 0, "errmsg" : "%s" % of})) + out(json.dumps({"ok": 0, "errmsg": "%s" % of})) return except StopIteration: # this is so stupid, there's no has_next? pass - - out(json.dumps({"results" : batch, "id" : cursor.id, "ok" : 1}, default=json_util.default)) + out(json.dumps({"results": batch, "id": cursor.id, "ok": 1}, default=json_util.default)) - def _insert(self, args, out, name = None, db = None, collection = None): + def _insert(self, args, out, name=None, db=None, collection=None): """ insert a doc """ @@ -342,34 +343,32 @@ def _insert(self, args, out, name = None, db = None, collection = None): return conn = self._get_connection(name) - if conn == None: + if conn is None: out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') return - if db == None or collection == None: + if db is None or collection is None: out('{"ok" : 0, "errmsg" : "db and collection must be defined"}') return - if "docs" not in args: + if "docs" not in args: out('{"ok" : 0, "errmsg" : "missing docs"}') return docs = self._get_son(args.getvalue('docs'), out) - if docs == None: + if docs is None: return safe = False if "safe" in args: safe = bool(args.getvalue("safe")) - result = {} - result['oids'] = conn[db][collection].insert(docs) + result = {'oids': conn[db][collection].insert(docs)} if safe: result['status'] = conn[db].last_status() out(json.dumps(result, default=json_util.default)) - def __safety_check(self, args, out, db): safe = False if "safe" in args: @@ -381,8 +380,7 @@ def __safety_check(self, args, out, db): else: out('{"ok" : 1}') - - def _update(self, args, out, name = None, db = None, collection = None): + def _update(self, args, out, name=None, db=None, collection=None): """ update a doc """ @@ -392,28 +390,28 @@ def _update(self, args, out, name = None, db = None, collection = None): return conn = self._get_connection(name) - if conn == None: + if conn is None: out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') return - if db == None or collection == None: + if db is None or collection is None: out('{"ok" : 0, "errmsg" : "db and collection must be defined"}') return - - if "criteria" not in args: + + if "criteria" not in args: out('{"ok" : 0, "errmsg" : "missing criteria"}') return criteria = self._get_son(args.getvalue('criteria'), out) - if criteria == None: + if criteria is None: return if "newobj" not in args: out('{"ok" : 0, "errmsg" : "missing newobj"}') return newobj = self._get_son(args.getvalue('newobj'), out) - if newobj == None: + if None == newobj: return - + upsert = False if "upsert" in args: upsert = bool(args.getvalue('upsert')) @@ -426,7 +424,7 @@ def _update(self, args, out, name = None, db = None, collection = None): self.__safety_check(args, out, conn[db]) - def _remove(self, args, out, name = None, db = None, collection = None): + def _remove(self, args, out, name=None, db=None, collection=None): """ remove docs """ @@ -436,25 +434,26 @@ def _remove(self, args, out, name = None, db = None, collection = None): return conn = self._get_connection(name) - if conn == None: + if conn is None: out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') return - if db == None or collection == None: + if db is None or collection is None: out('{"ok" : 0, "errmsg" : "db and collection must be defined"}') return - + criteria = {} if "criteria" in args: criteria = self._get_son(args.getvalue('criteria'), out) - if criteria == None: + if criteria is None: return - - result = conn[db][collection].remove(criteria) + + conn[db][collection].remove(criteria) self.__safety_check(args, out, conn[db]) - def _batch(self, args, out, name = None, db = None, collection = None): + # noinspection PyUnusedLocal + def _batch(self, args, out, name=None, db=None, collection=None): """ batch process commands """ @@ -464,7 +463,7 @@ def _batch(self, args, out, name = None, db = None, collection = None): return requests = self._get_son(args.getvalue('requests'), out) - if requests == None: + if requests is None: return out("[") @@ -478,7 +477,7 @@ def _batch(self, args, out, name = None, db = None, collection = None): method = "GET" if 'method' in request: method = request['method'] - + db = None if 'db' in request: db = request['db'] @@ -500,7 +499,7 @@ def _batch(self, args, out, name = None, db = None, collection = None): func = getattr(MongoHandler.mh, cmd, None) if callable(func): output = MongoFakeStream() - func(args, output.ostream, name = name, db = db, collection = collection) + func(args, output.ostream, name=name, db=db, collection=collection) if not first: out(",") first = False @@ -511,7 +510,7 @@ def _batch(self, args, out, name = None, db = None, collection = None): out("]") - + class MongoFakeStream: def __init__(self): self.str = "" @@ -522,6 +521,7 @@ def ostream(self, content): def get_ostream(self): return self.str + class MongoFakeFieldStorage: def __init__(self, args): self.args = args diff --git a/sleepymongoose/httpd.py b/sleepymongoose/httpd.py index 53629d5..865d772 100644 --- a/sleepymongoose/httpd.py +++ b/sleepymongoose/httpd.py @@ -14,14 +14,17 @@ from SocketServer import BaseServer from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer + from handlers import MongoHandler try: from OpenSSL import SSL except ImportError: + SSL = None pass -import os.path, socket +import os.path +import socket import urlparse import cgi import getopt @@ -36,22 +39,21 @@ try: urlparse.parse_qs except AttributeError: + # noinspection PyDeprecation urlparse.parse_qs = cgi.parse_qs - -class MongoServer(HTTPServer): - +class HTTPSServer(HTTPServer): pem = None def __init__(self, server_address, HandlerClass): BaseServer.__init__(self, server_address, HandlerClass) ctx = SSL.Context(SSL.SSLv23_METHOD) - fpem = MongoServer.pem + fpem = HTTPSServer.pem ctx.use_privatekey_file(fpem) ctx.use_certificate_file(fpem) - + self.socket = SSL.Connection(ctx, socket.socket(self.address_family, self.socket_type)) self.server_bind() @@ -59,21 +61,22 @@ def __init__(self, server_address, HandlerClass): class MongoHTTPRequest(BaseHTTPRequestHandler): - - mimetypes = { "html" : "text/html", - "htm" : "text/html", - "gif" : "image/gif", - "jpg" : "image/jpeg", - "png" : "image/png", - "json" : "application/json", - "css" : "text/css", - "js" : "text/javascript", - "ico" : "image/vnd.microsoft.icon" } + mimetypes = {"html": "text/html", + "htm": "text/html", + "gif": "image/gif", + "jpg": "image/jpeg", + "png": "image/png", + "json": "application/json", + "css": "text/css", + "js": "text/javascript", + "ico": "image/vnd.microsoft.icon"} docroot = "." + host = "localhost" + port = 27080 mongos = [] response_headers = [] - jsonp_callback = None; + jsonp_callback = None def _parse_call(self, uri): """ @@ -86,22 +89,21 @@ def _parse_call(self, uri): # operations always start with _ if parts[-1][0] != '_': - return (None, None, None) + return None, None, None if len(parts) == 1: - return ("admin", None, parts[0]) + return "admin", None, parts[0] elif len(parts) == 2: - return (parts[0], None, parts[1]) + return parts[0], None, parts[1] else: - return (parts[0], ".".join(parts[1:-1]), parts[-1]) - + return parts[0], ".".join(parts[1:-1]), parts[-1] def call_handler(self, uri, args): """ execute something """ (db, collection, func_name) = self._parse_call(uri) - if db == None or func_name == None: - self.send_error(404, 'Script Not Found: '+uri) + if db is None or func_name is None: + self.send_error(404, 'Script Not Found: ' + uri) return name = None @@ -117,7 +119,7 @@ def call_handler(self, uri, args): self.jsonp_callback = args["callback"][0] else: self.jsonp_callback = args.getvalue("callback") - + func = getattr(MongoHandler.mh, func_name, None) if callable(func): self.send_response(200, 'OK') @@ -127,19 +129,19 @@ def call_handler(self, uri, args): self.end_headers() if self.jsonp_callback: - func(args, self.prependJSONPCallback, name = name, db = db, collection = collection) + func(args, self.prependJSONPCallback, name=name, db=db, collection=collection) else: - func(args, self.wfile.write, name = name, db = db, collection = collection) + func(args, self.wfile.write, name=name, db=db, collection=collection) return else: - self.send_error(404, 'Script Not Found: '+uri) - return - - def prependJSONPCallback(self, str): - jsonp_output = '%s(' % self.jsonp_callback + str + ')' - self.wfile.write( jsonp_output ) - + self.send_error(404, 'Script Not Found: ' + uri) + return + + def prependJSONPCallback(self, s): + jsonp_output = '%s(' % self.jsonp_callback + s + ')' + self.wfile.write(jsonp_output) + # TODO: check for ..s def process_uri(self, method): if method == "GET": @@ -148,8 +150,8 @@ def process_uri(self, method): uri = self.path if 'Content-Type' in self.headers: args = cgi.FieldStorage(fp=self.rfile, headers=self.headers, - environ={'REQUEST_METHOD':'POST', - 'CONTENT_TYPE':self.headers['Content-Type']}) + environ={'REQUEST_METHOD': 'POST', + 'CONTENT_TYPE': self.headers['Content-Type']}) else: self.send_response(100, "Continue") self.send_header('Content-type', MongoHTTPRequest.mimetypes['json']) @@ -158,8 +160,7 @@ def process_uri(self, method): self.end_headers() self.wfile.write('{"ok" : 0, "errmsg" : "100-continue msgs not handled yet"}') - return (None, None, None) - + return None, None, None uri = uri.strip('/') @@ -167,26 +168,24 @@ def process_uri(self, method): if len(uri) == 0: uri = "index.html" - (temp, dot, type) = uri.rpartition('.') + (temp, dot, t) = uri.rpartition('.') # if we have a collection name with a dot, don't use that dot for type if len(dot) == 0 or uri.find('/') != -1: - type = "" + t = "" - return (uri, args, type) + return uri, args, t + def do_GET(self): + (uri, args, t) = self.process_uri("GET") - def do_GET(self): - (uri, args, type) = self.process_uri("GET") - - # serve up a plain file - if len(type) != 0: - if type in MongoHTTPRequest.mimetypes and os.path.exists(MongoHTTPRequest.docroot+uri): + if len(t) != 0: + if t in MongoHTTPRequest.mimetypes and os.path.exists(MongoHTTPRequest.docroot + uri): - fh = open(MongoHTTPRequest.docroot+uri, 'r') + fh = open(MongoHTTPRequest.docroot + uri, 'r') self.send_response(200, 'OK') - self.send_header('Content-type', MongoHTTPRequest.mimetypes[type]) + self.send_header('Content-type', MongoHTTPRequest.mimetypes[t]) for header in self.response_headers: self.send_header(header[0], header[1]) self.end_headers() @@ -197,7 +196,7 @@ def do_GET(self): return else: - self.send_error(404, 'File Not Found: '+uri) + self.send_error(404, 'File Not Found: ' + uri) return @@ -208,23 +207,25 @@ def do_GET(self): args = {} self.call_handler(uri, args) - #self.wfile.write( self.path ) + # self.wfile.write( self.path ) def do_POST(self): - (uri, args, type) = self.process_uri("POST") - if uri == None: + (uri, args, t) = self.process_uri("POST") + if uri is None: return self.call_handler(uri, args) @staticmethod - def serve_forever(port): + def serve_forever(host, port): + global server + print "\n=================================" print "| MongoDB REST Server |" print "=================================\n" - if MongoServer.pem == None: + if HTTPSServer.pem is None: try: - server = HTTPServer(('', port), MongoHTTPRequest) + server = HTTPServer((host, port), MongoHTTPRequest) except socket.error, (value, message): if value == 98: print "could not bind to localhost:%d... is sleepy.mongoose already running?\n" % port @@ -233,20 +234,29 @@ def serve_forever(port): return else: print "--------Secure Connection--------\n" - server = MongoServer(('', port), MongoHTTPSRequest) + server = HTTPSServer((host, port), MongoHTTPSRequest) MongoHandler.mh = MongoHandler(MongoHTTPRequest.mongos) - - print "listening for connections on http://localhost:27080\n" + + print "listening for connections on http://%s:%d\n" % (host, port) try: server.serve_forever() except KeyboardInterrupt: - print "\nShutting down the server..." - server.socket.close() - print "\nGood bye!\n" + pass + + print "\nShutting down the server..." + server.socket.close() + print "\nGood bye!\n" class MongoHTTPSRequest(MongoHTTPRequest): + def __init__(self, request, client_address, server): + self.connection = None + self.rfile = None + self.wfile = None + MongoHTTPRequest.__init__(self, request, client_address, server) + + # noinspection PyProtectedMember def setup(self): self.connection = self.request self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) @@ -254,36 +264,57 @@ def setup(self): def usage(): - print "python httpd.py [-x] [-d docroot/dir] [-s certificate.pem] [-m list,of,mongods]" + print "python httpd.py [-x] [-d docroot/dir] [-s certificate.pem] [-m list,of,mongods] [-h host] [-p port]" print "\t-x|--xorigin\tAllow cross-origin http requests" print "\t-d|--docroot\tlocation from which to load files" print "\t-s|--secure\tlocation of .pem file if ssl is desired" print "\t-m|--mongos\tcomma-separated list of mongo servers to connect to" + print "\t-h|--host\tlistening host" + print "\t-p|--port\tlistening port" def main(): - try: - opts, args = getopt.getopt(sys.argv[1:], "xd:s:m:", ["xorigin", "docroot=", - "secure=", "mongos="]) + import signal + # Signal handling + # noinspection PyUnusedLocal + def signalHandler(sigNum, frame): + global server + signal.default_int_handler(sigNum) + server.server_close() + + signal.signal(signal.SIGINT, signalHandler) + signal.signal(signal.SIGTERM, signalHandler) + signal.signal(signal.SIGABRT, signalHandler) + + try: + opts, args = getopt.getopt(sys.argv[1:], "xd:s:m:h:p:", ["xorigin", "docroot=", "secure=", "mongos=", + "host=", "port=", "help"]) for o, a in opts: + if o == "--help": + usage() + sys.exit(0) if o == "-d" or o == "--docroot": if not a.endswith('/'): - a = a+'/' + a += '/' MongoHTTPRequest.docroot = a if o == "-s" or o == "--secure": - MongoServer.pem = a + HTTPSServer.pem = a if o == "-m" or o == "--mongos": MongoHTTPRequest.mongos = a.split(',') if o == "-x" or o == "--xorigin": - MongoHTTPRequest.response_headers.append(("Access-Control-Allow-Origin","*")) + MongoHTTPRequest.response_headers.append(("Access-Control-Allow-Origin", "*")) + if o == "-h" or o == "--host": + MongoHTTPRequest.host = a + if o == "-p" or o == "--port": + MongoHTTPRequest.port = int(a) except getopt.GetoptError: print "error parsing cmd line args." usage() sys.exit(2) - MongoHTTPRequest.serve_forever(27080) + MongoHTTPRequest.serve_forever(MongoHTTPRequest.host, MongoHTTPRequest.port) + if __name__ == "__main__": main() - diff --git a/t/get.py b/t/get.py deleted file mode 100644 index f29707d..0000000 --- a/t/get.py +++ /dev/null @@ -1,66 +0,0 @@ -from restclient import GET, POST - -import json -import unittest - -class TestGET(unittest.TestCase): - - def setUp(self): - POST("http://localhost:27080/_connect", - params = {'server' : 'localhost:27017'}) - self._drop_collection() - - def _drop_collection(self): - str = POST("http://localhost:27080/test/_cmd", - params = {'cmd' : '{"drop" : "mongoose"}'}) - - def test_hello(self): - str = GET("http://localhost:27080/_hello") - - self.assertEquals(type(str).__name__, "str") - - obj = json.loads(str) - - self.assertEquals(obj['ok'], 1) - self.assertEquals(obj['msg'], "Uh, we had a slight weapons "+ - "malfunction, but uh... everything's perfectly "+ - "all right now. We're fine. We're all fine here "+ - "now, thank you. How are you?") - - def test_find(self): - POST("http://localhost:27080/test/mongoose/_insert", - params={'docs' : '[{"x" : 1},{"x" : 2},{"x" : 3}]'}, - async = False) - - str = GET("http://localhost:27080/test/mongoose/_find") - - self.assertEquals(type(str).__name__, "str") - - obj = json.loads(str) - - self.assertEquals(obj['ok'], 1, str) - self.assertEquals(type(obj['id']).__name__, "int", str) - self.assertEquals(len(obj['results']), 3, str) - - - def test_find_sort(self): - POST("http://localhost:27080/test/mongoose/_insert", - params={'docs' : '[{"x" : 1},{"x" : 2},{"x" : 3}]'}, - async = False) - - str = GET("http://localhost:27080/test/mongoose/_find", - {"sort" : '{"x" : -1}'}) - - self.assertEquals(type(str).__name__, "str") - - obj = json.loads(str) - - self.assertEquals(obj['results'][0]['x'], 3, str) - self.assertEquals(obj['results'][1]['x'], 2, str) - self.assertEquals(obj['results'][2]['x'], 1, str) - - - - -if __name__ == '__main__': - unittest.main() diff --git a/t/post.py b/t/post.py deleted file mode 100644 index 3c3572b..0000000 --- a/t/post.py +++ /dev/null @@ -1,226 +0,0 @@ -from restclient import GET, POST - -import json -import unittest - -class TestPOST(unittest.TestCase): - - def setUp(self): - POST("http://localhost:27080/_connect") - self._drop_collection() - - def _drop_collection(self): - str = POST("http://localhost:27080/test/_cmd", - params = {'cmd' : '{"drop" : "mongoose"}'}) - - def test_insert_err1(self): - str = POST("http://localhost:27080/_insert", - params = {'docs' : '[{"foo" : "bar"}]'}, - async = False ) - - self.assertEquals(type(str).__name__, "str") - - obj = json.loads(str) - - self.assertEquals(obj['ok'], 0) - self.assertEquals(obj['errmsg'], 'db and collection must be defined') - - def test_insert_err2(self): - str = POST("http://localhost:27080/test/_insert", - params = {'docs' : '[{"foo" : "bar"}]'}, - async = False ) - - self.assertEquals(type(str).__name__, "str") - - obj = json.loads(str) - - self.assertEquals(obj['ok'], 0) - self.assertEquals(obj['errmsg'], 'db and collection must be defined') - - def test_insert_err3(self): - str = POST("http://localhost:27080/test/mongoose/_insert", - async = False ) - - self.assertEquals(type(str).__name__, "str") - - obj = json.loads(str) - - self.assertEquals(obj['ok'], 0) - self.assertEquals(obj['errmsg'], 'missing docs') - - def test_insert(self): - str = POST("http://localhost:27080/test/mongoose/_insert", - params = {'docs' : '[{"foo" : "bar"}]'}, - async = False ) - - self.assertEquals(type(str).__name__, "str") - - obj = json.loads(str) - - self.assertEquals(type(obj['oids'][0]['$oid']).__name__, "unicode") - - def test_safe_insert(self): - str = POST("http://localhost:27080/test/mongoose/_insert", - params = {'docs' : '[{"foo" : "bar"}]', 'safe' : 1}, - async = False ) - - self.assertEquals(type(str).__name__, "str") - - obj = json.loads(str) - - self.assertEquals(type(obj['oids'][0]['$oid']).__name__, "unicode") - self.assertEquals(obj['status']['ok'], 1) - self.assertEquals(obj['status']['err'], None) - - def test_safe_insert_err1(self): - str = POST("http://localhost:27080/test/mongoose/_insert", - params = {'docs' : '[{"_id" : "bar"}, {"_id" : "bar"}]', 'safe' : 1}, - async = False ) - - self.assertEquals(type(str).__name__, "str") - - obj = json.loads(str) - - self.assertEquals(obj['status']['ok'], 1) - self.assertEquals(obj['status']['code'], 11000) - - def test_update_err1(self): - str = POST("http://localhost:27080/_update", - async = False ) - - self.assertEquals(type(str).__name__, "str") - - obj = json.loads(str) - - self.assertEquals(obj['ok'], 0) - self.assertEquals(obj['errmsg'], 'db and collection must be defined') - - def test_update_err2(self): - str = POST("http://localhost:27080/test/_update", - async = False ) - - self.assertEquals(type(str).__name__, "str") - - obj = json.loads(str) - - self.assertEquals(obj['ok'], 0) - self.assertEquals(obj['errmsg'], 'db and collection must be defined') - - def test_update_err3(self): - str = POST("http://localhost:27080/test/mongoose/_update", - async = False ) - - self.assertEquals(type(str).__name__, "str") - - obj = json.loads(str) - - self.assertEquals(obj['ok'], 0) - self.assertEquals(obj['errmsg'], 'missing criteria') - - def test_update_err4(self): - str = POST("http://localhost:27080/test/mongoose/_update", - params = {"criteria" : "{}"}, - async = False ) - - self.assertEquals(type(str).__name__, "str") - - obj = json.loads(str) - - self.assertEquals(obj['ok'], 0) - self.assertEquals(obj['errmsg'], 'missing newobj') - - def test_update(self): - str = POST("http://localhost:27080/test/mongoose/_update", - params = {"criteria" : "{}", "newobj" : '{"$set" : {"x" : 1}}'}, - async = False ) - - self.assertEquals(type(str).__name__, "str") - - obj = json.loads(str) - - self.assertEquals(obj['ok'], 1, str) - - def test_safe(self): - str = POST("http://localhost:27080/test/mongoose/_update", - params = {"criteria" : "{}", "newobj" : '{"$set" : {"x" : 1}}', "safe" : "1"}, - async = False ) - - self.assertEquals(type(str).__name__, "str") - - obj = json.loads(str) - - self.assertEquals(obj['ok'], 1) - self.assertEquals(obj['n'], 0) - self.assertEquals(obj['err'], None) - - def test_upsert(self): - str = POST("http://localhost:27080/test/mongoose/_update", - params = {"criteria" : "{}", "newobj" : '{"$set" : {"x" : 1}}', "upsert" : "1", "safe" : "1"}, - async = False ) - - self.assertEquals(type(str).__name__, "str") - - obj = json.loads(str) - - self.assertEquals(obj['ok'], 1, str) - self.assertEquals(obj['n'], 1, str) - - str = GET("http://localhost:27080/test/mongoose/_find") - obj = json.loads(str) - - self.assertEquals(obj['ok'], 1, str) - self.assertEquals(obj['results'][0]['x'], 1, str) - - def test_multi(self): - POST("http://localhost:27080/test/mongoose/_insert", - params = {"docs" : '[{"x" : 1},{"x" : 1},{"x" : 1},{"y" : 1}]'}, - async = False ) - - str = POST("http://localhost:27080/test/mongoose/_update", - params = {"criteria" : '{"x" : 1}', "newobj" : '{"$set" : {"x" : 2}}', "multi" : "1", "safe" : "1"}, - async = False ) - - obj = json.loads(str) - - self.assertEquals(obj['ok'], 1, str) - self.assertEquals(obj['n'], 3, str) - - def test_remove(self): - POST("http://localhost:27080/test/mongoose/_insert", - params = {"docs" : '[{"x" : 1},{"x" : 1},{"x" : 1},{"y" : 1}]'}, - async = False ) - - str = POST("http://localhost:27080/test/mongoose/_remove", - async = False ) - - obj = json.loads(str) - - self.assertEquals(obj['ok'], 1, str) - - - def test_remove_safe(self): - POST("http://localhost:27080/test/mongoose/_insert", - params = {"docs" : '[{"x" : 1},{"x" : 1},{"x" : 1},{"y" : 1}]'}, - async = False ) - - str = POST("http://localhost:27080/test/mongoose/_remove", - params = {"criteria" : '{"x" : 1}', "safe" : 1}, - async = False ) - - obj = json.loads(str) - - self.assertEquals(obj['ok'], 1, str) - self.assertEquals(obj['n'], 3, str) - - str = POST("http://localhost:27080/test/mongoose/_remove", - params = {"safe" : "1"}, - async = False ) - - obj = json.loads(str) - - self.assertEquals(obj['ok'], 1, str) - self.assertEquals(obj['n'], 1, str) - - -if __name__ == '__main__': - unittest.main() diff --git a/test/get.py b/test/get.py new file mode 100644 index 0000000..3e2a981 --- /dev/null +++ b/test/get.py @@ -0,0 +1,64 @@ +# noinspection PyPackageRequirements +from restclient import GET, POST + +import json +import unittest + + +class TestGET(unittest.TestCase): + def setUp(self): + POST("http://localhost:27080/_connect", + params={'server': 'localhost:27017'}) + self._drop_collection() + + def _drop_collection(self): + POST("http://localhost:27080/test/_cmd", + params={'cmd': '{"drop" : "mongoose"}'}) + + def test_hello(self): + s = GET("http://localhost:27080/_hello") + + self.assertEquals(type(s).__name__, "str") + + obj = json.loads(s) + + self.assertEquals(obj['ok'], 1) + self.assertEquals(obj['msg'], "Uh, we had a slight weapons " + + "malfunction, but uh... everything's perfectly " + + "all right now. We're fine. We're all fine here " + + "now, thank you. How are you?") + + def test_find(self): + POST("http://localhost:27080/test/mongoose/_insert", + params={'docs': '[{"x" : 1},{"x" : 2},{"x" : 3}]'}, + async=False) + + s = GET("http://localhost:27080/test/mongoose/_find") + + self.assertEquals(type(s).__name__, "str") + + obj = json.loads(s) + + self.assertEquals(obj['ok'], 1, s) + self.assertEquals(type(obj['id']).__name__, "int", s) + self.assertEquals(len(obj['results']), 3, s) + + def test_find_sort(self): + POST("http://localhost:27080/test/mongoose/_insert", + params={'docs': '[{"x" : 1},{"x" : 2},{"x" : 3}]'}, + async=False) + + s = GET("http://localhost:27080/test/mongoose/_find", + {"sort": '{"x" : -1}'}) + + self.assertEquals(type(s).__name__, "str") + + obj = json.loads(s) + + self.assertEquals(obj['results'][0]['x'], 3, s) + self.assertEquals(obj['results'][1]['x'], 2, s) + self.assertEquals(obj['results'][2]['x'], 1, s) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/post.py b/test/post.py new file mode 100644 index 0000000..62efdb9 --- /dev/null +++ b/test/post.py @@ -0,0 +1,226 @@ +# noinspection PyPackageRequirements +from restclient import GET, POST + +import json +import unittest + + +class TestPOST(unittest.TestCase): + def setUp(self): + POST("http://localhost:27080/_connect") + self._drop_collection() + + def _drop_collection(self): + POST("http://localhost:27080/test/_cmd", + params={'cmd': '{"drop" : "mongoose"}'}) + + def test_insert_err1(self): + s = POST("http://localhost:27080/_insert", + params={'docs': '[{"foo" : "bar"}]'}, + async=False) + + self.assertEquals(type(s).__name__, "str") + + obj = json.loads(s) + + self.assertEquals(obj['ok'], 0) + self.assertEquals(obj['errmsg'], 'db and collection must be defined') + + def test_insert_err2(self): + s = POST("http://localhost:27080/test/_insert", + params={'docs': '[{"foo" : "bar"}]'}, + async=False) + + self.assertEquals(type(s).__name__, "str") + + obj = json.loads(s) + + self.assertEquals(obj['ok'], 0) + self.assertEquals(obj['errmsg'], 'db and collection must be defined') + + def test_insert_err3(self): + s = POST("http://localhost:27080/test/mongoose/_insert", + async=False) + + self.assertEquals(type(s).__name__, "str") + + obj = json.loads(s) + + self.assertEquals(obj['ok'], 0) + self.assertEquals(obj['errmsg'], 'missing docs') + + def test_insert(self): + s = POST("http://localhost:27080/test/mongoose/_insert", + params={'docs': '[{"foo" : "bar"}]'}, + async=False) + + self.assertEquals(type(s).__name__, "str") + + obj = json.loads(s) + + self.assertEquals(type(obj['oids'][0]['$oid']).__name__, "unicode") + + def test_safe_insert(self): + s = POST("http://localhost:27080/test/mongoose/_insert", + params={'docs': '[{"foo" : "bar"}]', 'safe': 1}, + async=False) + + self.assertEquals(type(s).__name__, "str") + + obj = json.loads(s) + + self.assertEquals(type(obj['oids'][0]['$oid']).__name__, "unicode") + self.assertEquals(obj['status']['ok'], 1) + self.assertEquals(obj['status']['err'], None) + + def test_safe_insert_err1(self): + s = POST("http://localhost:27080/test/mongoose/_insert", + params={'docs': '[{"_id" : "bar"}, {"_id" : "bar"}]', 'safe': 1}, + async=False) + + self.assertEquals(type(s).__name__, "str") + + obj = json.loads(s) + + self.assertEquals(obj['status']['ok'], 1) + self.assertEquals(obj['status']['code'], 11000) + + def test_update_err1(self): + s = POST("http://localhost:27080/_update", + async=False) + + self.assertEquals(type(s).__name__, "str") + + obj = json.loads(s) + + self.assertEquals(obj['ok'], 0) + self.assertEquals(obj['errmsg'], 'db and collection must be defined') + + def test_update_err2(self): + s = POST("http://localhost:27080/test/_update", + async=False) + + self.assertEquals(type(s).__name__, "str") + + obj = json.loads(s) + + self.assertEquals(obj['ok'], 0) + self.assertEquals(obj['errmsg'], 'db and collection must be defined') + + def test_update_err3(self): + s = POST("http://localhost:27080/test/mongoose/_update", + async=False) + + self.assertEquals(type(s).__name__, "str") + + obj = json.loads(s) + + self.assertEquals(obj['ok'], 0) + self.assertEquals(obj['errmsg'], 'missing criteria') + + def test_update_err4(self): + s = POST("http://localhost:27080/test/mongoose/_update", + params={"criteria": "{}"}, + async=False) + + self.assertEquals(type(s).__name__, "str") + + obj = json.loads(s) + + self.assertEquals(obj['ok'], 0) + self.assertEquals(obj['errmsg'], 'missing newobj') + + def test_update(self): + s = POST("http://localhost:27080/test/mongoose/_update", + params={"criteria": "{}", "newobj": '{"$set" : {"x" : 1}}'}, + async=False) + + self.assertEquals(type(s).__name__, "str") + + obj = json.loads(s) + + self.assertEquals(obj['ok'], 1, s) + + def test_safe(self): + s = POST("http://localhost:27080/test/mongoose/_update", + params={"criteria": "{}", "newobj": '{"$set" : {"x" : 1}}', "safe": "1"}, + async=False) + + self.assertEquals(type(s).__name__, "str") + + obj = json.loads(s) + + self.assertEquals(obj['ok'], 1) + self.assertEquals(obj['n'], 0) + self.assertEquals(obj['err'], None) + + def test_upsert(self): + s = POST("http://localhost:27080/test/mongoose/_update", + params={"criteria": "{}", "newobj": '{"$set" : {"x" : 1}}', "upsert": "1", "safe": "1"}, + async=False) + + self.assertEquals(type(s).__name__, "str") + + obj = json.loads(s) + + self.assertEquals(obj['ok'], 1, s) + self.assertEquals(obj['n'], 1, s) + + s = GET("http://localhost:27080/test/mongoose/_find") + obj = json.loads(s) + + self.assertEquals(obj['ok'], 1, s) + self.assertEquals(obj['results'][0]['x'], 1, s) + + def test_multi(self): + POST("http://localhost:27080/test/mongoose/_insert", + params={"docs": '[{"x" : 1},{"x" : 1},{"x" : 1},{"y" : 1}]'}, + async=False) + + s = POST("http://localhost:27080/test/mongoose/_update", + params={"criteria": '{"x" : 1}', "newobj": '{"$set" : {"x" : 2}}', "multi": "1", "safe": "1"}, + async=False) + + obj = json.loads(s) + + self.assertEquals(obj['ok'], 1, s) + self.assertEquals(obj['n'], 3, s) + + def test_remove(self): + POST("http://localhost:27080/test/mongoose/_insert", + params={"docs": '[{"x" : 1},{"x" : 1},{"x" : 1},{"y" : 1}]'}, + async=False) + + s = POST("http://localhost:27080/test/mongoose/_remove", + async=False) + + obj = json.loads(s) + + self.assertEquals(obj['ok'], 1, s) + + def test_remove_safe(self): + POST("http://localhost:27080/test/mongoose/_insert", + params={"docs": '[{"x" : 1},{"x" : 1},{"x" : 1},{"y" : 1}]'}, + async=False) + + s = POST("http://localhost:27080/test/mongoose/_remove", + params={"criteria": '{"x" : 1}', "safe": 1}, + async=False) + + obj = json.loads(s) + + self.assertEquals(obj['ok'], 1, s) + self.assertEquals(obj['n'], 3, s) + + s = POST("http://localhost:27080/test/mongoose/_remove", + params={"safe": "1"}, + async=False) + + obj = json.loads(s) + + self.assertEquals(obj['ok'], 1, s) + self.assertEquals(obj['n'], 1, s) + + +if __name__ == '__main__': + unittest.main() From cbdd8265fef24bf2db09fdcc060697ba9a8dc0e4 Mon Sep 17 00:00:00 2001 From: Adam Wallner Date: Mon, 9 Feb 2015 18:39:47 +0100 Subject: [PATCH 2/8] bson module is not needed and pymongo must use its own version --- requirements.txt | 1 - sleepymongoose/handlers.py | 8 +++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index a24ce42..a25b00e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ -bson simplejson pymongo diff --git a/sleepymongoose/handlers.py b/sleepymongoose/handlers.py index 2daf59a..7d09e4c 100644 --- a/sleepymongoose/handlers.py +++ b/sleepymongoose/handlers.py @@ -12,10 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +# noinspection PyPackageRequirements from bson.son import SON -# from pymongo import Connection, ASCENDING, DESCENDING -# from pymongo.errors import ConnectionFailure, ConfigurationError, OperationFailure, AutoReconnect -# from bson import json_util +# noinspection PyPackageRequirements +from bson import json_util +from pymongo import Connection, ASCENDING, DESCENDING +from pymongo.errors import ConnectionFailure, ConfigurationError, OperationFailure, AutoReconnect import re From 9915f94e1d0ef957e5585113397e4949177bd918 Mon Sep 17 00:00:00 2001 From: Adam Wallner Date: Wed, 11 Feb 2015 09:03:23 +0100 Subject: [PATCH 3/8] From now it is the same to send parameters as GET or POST method --- sleepymongoose/handlers.py | 82 +++++++------------------------------- sleepymongoose/httpd.py | 34 ++++++++-------- 2 files changed, 32 insertions(+), 84 deletions(-) diff --git a/sleepymongoose/handlers.py b/sleepymongoose/handlers.py index 7d09e4c..939c03d 100644 --- a/sleepymongoose/handlers.py +++ b/sleepymongoose/handlers.py @@ -36,7 +36,7 @@ def __init__(self, mongos): self.connections = {} for host in mongos: - args = MongoFakeFieldStorage({"server": host}) + args = {"server": host} out = MongoFakeStream() if len(mongos) == 1: @@ -116,7 +116,7 @@ def _cmd(self, args, out, name=None, db=None, collection=None): out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') return - cmd = self._get_son(args.getvalue('cmd'), out) + cmd = self._get_son(args['cmd'], out) if cmd is None: return @@ -132,7 +132,7 @@ def _cmd(self, args, out, name=None, db=None, collection=None): # debugging if result['ok'] == 0: - result['cmd'] = args.getvalue('cmd') + result['cmd'] = args['cmd'] out(json.dumps(result, default=json_util.default)) @@ -157,14 +157,9 @@ def _connect(self, args, out, name=None, db=None, collection=None): """ connect to a mongod """ - - if type(args).__name__ == 'dict': - out('{"ok" : 0, "errmsg" : "_connect must be a POST request"}') - return - if "server" in args: try: - uri = args.getvalue('server') + uri = args['server'] except Exception, e: print e out('{"ok" : 0, "errmsg" : "invalid server uri given"}') @@ -186,11 +181,6 @@ def _authenticate(self, args, out, name=None, db=None, collection=None): """ authenticate to the database. """ - - if type(args).__name__ == 'dict': - out('{"ok" : 0, "errmsg" : "_find must be a POST request"}') - return - conn = self._get_connection(name) if conn is None: out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') @@ -206,7 +196,7 @@ def _authenticate(self, args, out, name=None, db=None, collection=None): if 'password' not in args: out('{"ok" : 0, "errmsg" : "password must be defined"}') - if not conn[db].authenticate(args.getvalue('username'), args.getvalue('password')): + if not conn[db].authenticate(args['username'], args['password']): out('{"ok" : 0, "errmsg" : "authentication failed"}') else: out('{"ok" : 1}') @@ -215,11 +205,6 @@ def _find(self, args, out, name=None, db=None, collection=None): """ query the database. """ - - if type(args).__name__ != 'dict': - out('{"ok" : 0, "errmsg" : "_find must be a GET request"}') - return - conn = self._get_connection(name) if conn is None: out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') @@ -290,11 +275,6 @@ def _more(self, args, out, name=None, db=None, collection=None): """ Get more results from a cursor """ - - if type(args).__name__ != 'dict': - out('{"ok" : 0, "errmsg" : "_more must be a GET request"}') - return - if 'id' not in args: out('{"ok" : 0, "errmsg" : "no cursor id given"}') return @@ -339,11 +319,6 @@ def _insert(self, args, out, name=None, db=None, collection=None): """ insert a doc """ - - if type(args).__name__ == 'dict': - out('{"ok" : 0, "errmsg" : "_insert must be a POST request"}') - return - conn = self._get_connection(name) if conn is None: out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') @@ -357,13 +332,13 @@ def _insert(self, args, out, name=None, db=None, collection=None): out('{"ok" : 0, "errmsg" : "missing docs"}') return - docs = self._get_son(args.getvalue('docs'), out) + docs = self._get_son(args['docs'], out) if docs is None: return safe = False if "safe" in args: - safe = bool(args.getvalue("safe")) + safe = bool(args['safe']) result = {'oids': conn[db][collection].insert(docs)} if safe: @@ -374,7 +349,7 @@ def _insert(self, args, out, name=None, db=None, collection=None): def __safety_check(self, args, out, db): safe = False if "safe" in args: - safe = bool(args.getvalue("safe")) + safe = bool(args['safe']) if safe: result = db.last_status() @@ -386,11 +361,6 @@ def _update(self, args, out, name=None, db=None, collection=None): """ update a doc """ - - if type(args).__name__ == 'dict': - out('{"ok" : 0, "errmsg" : "_update must be a POST request"}') - return - conn = self._get_connection(name) if conn is None: out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') @@ -403,24 +373,24 @@ def _update(self, args, out, name=None, db=None, collection=None): if "criteria" not in args: out('{"ok" : 0, "errmsg" : "missing criteria"}') return - criteria = self._get_son(args.getvalue('criteria'), out) + criteria = self._get_son(args['criteria'], out) if criteria is None: return if "newobj" not in args: out('{"ok" : 0, "errmsg" : "missing newobj"}') return - newobj = self._get_son(args.getvalue('newobj'), out) + newobj = self._get_son(args['newobj'], out) if None == newobj: return upsert = False if "upsert" in args: - upsert = bool(args.getvalue('upsert')) + upsert = bool(args['upsert']) multi = False if "multi" in args: - multi = bool(args.getvalue('multi')) + multi = bool(args['multi']) conn[db][collection].update(criteria, newobj, upsert=upsert, multi=multi) @@ -430,11 +400,6 @@ def _remove(self, args, out, name=None, db=None, collection=None): """ remove docs """ - - if type(args).__name__ == 'dict': - out('{"ok" : 0, "errmsg" : "_remove must be a POST request"}') - return - conn = self._get_connection(name) if conn is None: out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') @@ -446,7 +411,7 @@ def _remove(self, args, out, name=None, db=None, collection=None): criteria = {} if "criteria" in args: - criteria = self._get_son(args.getvalue('criteria'), out) + criteria = self._get_son(args['criteria'], out) if criteria is None: return @@ -459,12 +424,7 @@ def _batch(self, args, out, name=None, db=None, collection=None): """ batch process commands """ - - if type(args).__name__ == 'dict': - out('{"ok" : 0, "errmsg" : "_batch must be a POST request"}') - return - - requests = self._get_son(args.getvalue('requests'), out) + requests = self._get_son(args['requests'], out) if requests is None: return @@ -495,9 +455,6 @@ def _batch(self, args, out, name=None, db=None, collection=None): if 'name' in args: name = args['name'] - if method == "POST": - args = MongoFakeFieldStorage(args) - func = getattr(MongoHandler.mh, cmd, None) if callable(func): output = MongoFakeStream() @@ -522,14 +479,3 @@ def ostream(self, content): def get_ostream(self): return self.str - - -class MongoFakeFieldStorage: - def __init__(self, args): - self.args = args - - def getvalue(self, key): - return self.args[key] - - def __contains__(self, key): - return key in self.args diff --git a/sleepymongoose/httpd.py b/sleepymongoose/httpd.py index 865d772..41a97a3 100644 --- a/sleepymongoose/httpd.py +++ b/sleepymongoose/httpd.py @@ -144,14 +144,22 @@ def prependJSONPCallback(self, s): # TODO: check for ..s def process_uri(self, method): - if method == "GET": - (uri, q, args) = self.path.partition('?') + # Parse GET parameters + (uri, q, args) = self.path.partition('?') + if len(args): + args = urlparse.parse_qs(args) else: - uri = self.path + args = {} + + if method == "POST": if 'Content-Type' in self.headers: - args = cgi.FieldStorage(fp=self.rfile, headers=self.headers, - environ={'REQUEST_METHOD': 'POST', - 'CONTENT_TYPE': self.headers['Content-Type']}) + print args + pargs = cgi.FieldStorage(fp=self.rfile, headers=self.headers, + environ={'REQUEST_METHOD': 'POST', + 'CONTENT_TYPE': self.headers['Content-Type']}) + # Convert to args + for k in pargs.keys(): + args[k] = pargs.getlist(k) else: self.send_response(100, "Continue") self.send_header('Content-type', MongoHTTPRequest.mimetypes['json']) @@ -200,14 +208,7 @@ def do_GET(self): return - # make sure args is an array of tuples - if len(args) != 0: - args = urlparse.parse_qs(args) - else: - args = {} - self.call_handler(uri, args) - # self.wfile.write( self.path ) def do_POST(self): (uri, args, t) = self.process_uri("POST") @@ -220,7 +221,7 @@ def serve_forever(host, port): global server print "\n=================================" - print "| MongoDB REST Server |" + print "| MongoDB HTTP Server |" print "=================================\n" if HTTPSServer.pem is None: @@ -250,11 +251,11 @@ def serve_forever(host, port): class MongoHTTPSRequest(MongoHTTPRequest): - def __init__(self, request, client_address, server): + def __init__(self, request, client_address, _server): self.connection = None self.rfile = None self.wfile = None - MongoHTTPRequest.__init__(self, request, client_address, server) + MongoHTTPRequest.__init__(self, request, client_address, _server) # noinspection PyProtectedMember def setup(self): @@ -316,5 +317,6 @@ def signalHandler(sigNum, frame): MongoHTTPRequest.serve_forever(MongoHTTPRequest.host, MongoHTTPRequest.port) + if __name__ == "__main__": main() From 5e59b0c03db4d9f9591fc395975f7f7a361f899d Mon Sep 17 00:00:00 2001 From: Adam Wallner Date: Wed, 11 Feb 2015 10:20:46 +0100 Subject: [PATCH 4/8] Disconnect command --- sleepymongoose/handlers.py | 17 +++++++++++++++++ sleepymongoose/httpd.py | 1 - 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/sleepymongoose/handlers.py b/sleepymongoose/handlers.py index 939c03d..a78680b 100644 --- a/sleepymongoose/handlers.py +++ b/sleepymongoose/handlers.py @@ -201,6 +201,23 @@ def _authenticate(self, args, out, name=None, db=None, collection=None): else: out('{"ok" : 1}') + # noinspection PyUnusedLocal + def _disconnect(self, args, out, name=None, db=None, collection=None): + """ + disconnect from the database + """ + conn = self._get_connection(name) + if conn is None: + out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') + return + + conn.disconnect() + del self.connections[name] + + out('{"ok" : 1}') + + print out + def _find(self, args, out, name=None, db=None, collection=None): """ query the database. diff --git a/sleepymongoose/httpd.py b/sleepymongoose/httpd.py index 41a97a3..123125d 100644 --- a/sleepymongoose/httpd.py +++ b/sleepymongoose/httpd.py @@ -153,7 +153,6 @@ def process_uri(self, method): if method == "POST": if 'Content-Type' in self.headers: - print args pargs = cgi.FieldStorage(fp=self.rfile, headers=self.headers, environ={'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': self.headers['Content-Type']}) From 92acb252e636c2caef378ac2305c51d5fa28841b Mon Sep 17 00:00:00 2001 From: Adam Wallner Date: Wed, 11 Feb 2015 10:23:13 +0100 Subject: [PATCH 5/8] unnecessary print --- sleepymongoose/handlers.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/sleepymongoose/handlers.py b/sleepymongoose/handlers.py index a78680b..374a9ed 100644 --- a/sleepymongoose/handlers.py +++ b/sleepymongoose/handlers.py @@ -216,8 +216,6 @@ def _disconnect(self, args, out, name=None, db=None, collection=None): out('{"ok" : 1}') - print out - def _find(self, args, out, name=None, db=None, collection=None): """ query the database. From 51ad4104ec7dd2ad8137f7459de4ffdb9f32080f Mon Sep 17 00:00:00 2001 From: Adam Wallner Date: Wed, 11 Feb 2015 15:50:20 +0100 Subject: [PATCH 6/8] - JSON print fix - Query parameters must be unique, no need to be a list, much cleaner code --- sleepymongoose/handlers.py | 2 +- sleepymongoose/httpd.py | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/sleepymongoose/handlers.py b/sleepymongoose/handlers.py index 374a9ed..76397de 100644 --- a/sleepymongoose/handlers.py +++ b/sleepymongoose/handlers.py @@ -97,7 +97,7 @@ def _get_son(self, s, out): try: obj = json.loads(s, object_hook=json_util.object_hook) except (ValueError, TypeError): - out('{"ok" : 0, "errmsg" : "couldn\'t parse json: %s"}' % s) + out('{"ok" : 0, "errmsg" : "couldn\'t parse json: %s"}' % str(s).replace('"', '\\"')) return None if not getattr(obj, '__iter__', False): diff --git a/sleepymongoose/httpd.py b/sleepymongoose/httpd.py index 123125d..076dd04 100644 --- a/sleepymongoose/httpd.py +++ b/sleepymongoose/httpd.py @@ -108,10 +108,7 @@ def call_handler(self, uri, args): name = None if "name" in args: - if type(args).__name__ == "dict": - name = args["name"][0] - else: - name = args.getvalue("name") + name = args["name"] self.jsonp_callback = None if "callback" in args: @@ -147,7 +144,7 @@ def process_uri(self, method): # Parse GET parameters (uri, q, args) = self.path.partition('?') if len(args): - args = urlparse.parse_qs(args) + args = dict(urlparse.parse_qsl(args)) else: args = {} @@ -158,7 +155,9 @@ def process_uri(self, method): 'CONTENT_TYPE': self.headers['Content-Type']}) # Convert to args for k in pargs.keys(): - args[k] = pargs.getlist(k) + args[k] = pargs.getvalue(k) + + print args else: self.send_response(100, "Continue") self.send_header('Content-type', MongoHTTPRequest.mimetypes['json']) From 33291c2e77c0ae08eec43a74ddf36b6328ec07e8 Mon Sep 17 00:00:00 2001 From: Adam Wallner Date: Mon, 23 Feb 2015 17:07:31 +0100 Subject: [PATCH 7/8] - ensure_index support for creating indices - insert_or_update, it can automatically create criteria from newobj by unique indexes specified in the object - Code cleanups --- README.md | 16 +++- sleepymongoose/handlers.py | 162 +++++++++++++++++++++++++------------ sleepymongoose/httpd.py | 6 +- 3 files changed, 127 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 6e42add..39e1c32 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,18 @@ Please note: all tools/ scripts in this repo are released for use "AS IS" withou Any use of these scripts and tools is at your own risk. There is no guarantee that they have been through thorough testing in a comparable environment and we are not responsible for any damage or data loss incurred with their use. You are responsible for reviewing and testing any scripts you run thoroughly before use in any non-testing environment. -See [the wiki](https://github.com/10gen-labs/sleepy.mongoose/wiki) for documentation. +See [the original wiki](https://github.com/10gen-labs/sleepy.mongoose/wiki) for documentation. + +This is a modified, optimised version of the original Sleepy Mongoose. +New features: + +* You can mix POST and GET requests, it is the same parameter in different transfer method. Post has more precendency. + +* You can specify listen host and port + +* Disconnect method (_disconnect) + +* Insert or update method (_insert_or_update), which is a shorcut to upsert update + +* Shell script to start/stop or run in debug mode (Unix/Linux) + diff --git a/sleepymongoose/handlers.py b/sleepymongoose/handlers.py index 76397de..5edb979 100644 --- a/sleepymongoose/handlers.py +++ b/sleepymongoose/handlers.py @@ -11,6 +11,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Modified by Adam Wallner - Bitbaro Mobile kft # noinspection PyPackageRequirements from bson.son import SON @@ -27,6 +29,10 @@ import simplejson as json +def esc(s): + return str(s).replace('"', '\\"') + + class MongoHandler: mh = None @@ -93,15 +99,15 @@ def sm_object_hook(obj): else: return json_util.object_hook(obj) - def _get_son(self, s, out): + def _get_json(self, s, out): try: obj = json.loads(s, object_hook=json_util.object_hook) except (ValueError, TypeError): - out('{"ok" : 0, "errmsg" : "couldn\'t parse json: %s"}' % str(s).replace('"', '\\"')) + out('{"ok": 0, "errmsg": "couldn\'t parse json: %s"}' % esc(s)) return None if not getattr(obj, '__iter__', False): - out('{"ok" : 0, "errmsg" : "type is not iterable: %s"}' % s) + out('{"ok": 0, "errmsg": "type is not iterable: %s"}' % s) return None return obj @@ -113,21 +119,21 @@ def _cmd(self, args, out, name=None, db=None, collection=None): conn = self._get_connection(name) if conn is None: - out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') + out('{"ok": 0, "errmsg": "couldn\'t get connection to mongo"}') return - cmd = self._get_son(args['cmd'], out) + cmd = self._get_json(args['cmd'], out) if cmd is None: return try: result = conn[db].command(cmd, check=False) except AutoReconnect: - out('{"ok" : 0, "errmsg" : "wasn\'t connected to the db and ' + - 'couldn\'t reconnect", "name" : "%s"}' % name) + out('{"ok": 0, "errmsg": "wasn\'t connected to the db and ' + + 'couldn\'t reconnect", "name": "%s"}' % name) return except (OperationFailure, error): - out('{"ok" : 0, "errmsg" : "%s"}' % error) + out('{"ok": 0, "errmsg": "%s"}' % error) return # debugging @@ -138,7 +144,7 @@ def _cmd(self, args, out, name=None, db=None, collection=None): # noinspection PyUnusedLocal def _hello(self, args, out, name=None, db=None, collection=None): - out('{"ok" : 1, "msg" : "Uh, we had a slight weapons malfunction, but ' + + out('{"ok": 1, "msg": "Uh, we had a slight weapons malfunction, but ' + 'uh... everything\'s perfectly all right now. We\'re fine. We\'re ' + 'all fine here now, thank you. How are you?"}') return @@ -162,7 +168,7 @@ def _connect(self, args, out, name=None, db=None, collection=None): uri = args['server'] except Exception, e: print e - out('{"ok" : 0, "errmsg" : "invalid server uri given"}') + out('{"ok": 0, "errmsg": "invalid server uri given"}') return else: uri = 'mongodb://localhost:27017' @@ -172,9 +178,9 @@ def _connect(self, args, out, name=None, db=None, collection=None): conn = self._get_connection(name, uri) if conn is not None: - out('{"ok" : 1, "server" : "%s", "name" : "%s"}' % (uri, name)) + out('{"ok": 1, "server": "%s", "name": "%s"}' % (uri, name)) else: - out('{"ok" : 0, "errmsg" : "could not connect", "server" : "%s", "name" : "%s"}' % (uri, name)) + out('{"ok": 0, "errmsg": "could not connect", "server": "%s", "name": "%s"}' % (uri, name)) # noinspection PyUnusedLocal def _authenticate(self, args, out, name=None, db=None, collection=None): @@ -183,23 +189,23 @@ def _authenticate(self, args, out, name=None, db=None, collection=None): """ conn = self._get_connection(name) if conn is None: - out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') + out('{"ok": 0, "errmsg": "couldn\'t get connection to mongo"}') return if db is None: - out('{"ok" : 0, "errmsg" : "db must be defined"}') + out('{"ok": 0, "errmsg": "db must be defined"}') return if 'username' not in args: - out('{"ok" : 0, "errmsg" : "username must be defined"}') + out('{"ok": 0, "errmsg": "username must be defined"}') if 'password' not in args: - out('{"ok" : 0, "errmsg" : "password must be defined"}') + out('{"ok": 0, "errmsg": "password must be defined"}') if not conn[db].authenticate(args['username'], args['password']): - out('{"ok" : 0, "errmsg" : "authentication failed"}') + out('{"ok": 0, "errmsg": "authentication failed"}') else: - out('{"ok" : 1}') + out('{"ok": 1}') # noinspection PyUnusedLocal def _disconnect(self, args, out, name=None, db=None, collection=None): @@ -208,13 +214,13 @@ def _disconnect(self, args, out, name=None, db=None, collection=None): """ conn = self._get_connection(name) if conn is None: - out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') + out('{"ok": 0, "errmsg": "couldn\'t get connection to mongo"}') return conn.disconnect() del self.connections[name] - out('{"ok" : 1}') + out('{"ok": 1}') def _find(self, args, out, name=None, db=None, collection=None): """ @@ -222,22 +228,22 @@ def _find(self, args, out, name=None, db=None, collection=None): """ conn = self._get_connection(name) if conn is None: - out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') + out('{"ok": 0, "errmsg": "couldn\'t get connection to mongo"}') return if db is None or collection is None: - out('{"ok" : 0, "errmsg" : "db and collection must be defined"}') + out('{"ok": 0, "errmsg": "db and collection must be defined"}') return criteria = {} if 'criteria' in args: - criteria = self._get_son(args['criteria'][0], out) + criteria = self._get_json(args['criteria'][0], out) if None == criteria: return fields = None if 'fields' in args: - fields = self._get_son(args['fields'][0], out) + fields = self._get_json(args['fields'][0], out) if fields is None: return @@ -252,7 +258,7 @@ def _find(self, args, out, name=None, db=None, collection=None): cursor = conn[db][collection].find(spec=criteria, fields=fields, limit=limit, skip=skip) if 'sort' in args: - sort = self._get_son(args['sort'][0], out) + sort = self._get_json(args['sort'][0], out) if sort is None: return @@ -291,14 +297,14 @@ def _more(self, args, out, name=None, db=None, collection=None): Get more results from a cursor """ if 'id' not in args: - out('{"ok" : 0, "errmsg" : "no cursor id given"}') + out('{"ok": 0, "errmsg": "no cursor id given"}') return _id = int(args["id"][0]) cursors = getattr(self, "cursors") if _id not in cursors: - out('{"ok" : 0, "errmsg" : "couldn\'t find the cursor with id %d"}' % _id) + out('{"ok": 0, "errmsg": "couldn\'t find the cursor with id %d"}' % _id) return cursor = cursors[_id] @@ -336,18 +342,18 @@ def _insert(self, args, out, name=None, db=None, collection=None): """ conn = self._get_connection(name) if conn is None: - out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') + out('{"ok": 0, "errmsg": "couldn\'t get connection to mongo"}') return if db is None or collection is None: - out('{"ok" : 0, "errmsg" : "db and collection must be defined"}') + out('{"ok": 0, "errmsg": "db and collection must be defined"}') return if "docs" not in args: - out('{"ok" : 0, "errmsg" : "missing docs"}') + out('{"ok": 0, "errmsg": "missing docs"}') return - docs = self._get_son(args['docs'], out) + docs = self._get_json(args['docs'], out) if docs is None: return @@ -370,7 +376,7 @@ def __safety_check(self, args, out, db): result = db.last_status() out(json.dumps(result, default=json_util.default)) else: - out('{"ok" : 1}') + out('{"ok": 1}') def _update(self, args, out, name=None, db=None, collection=None): """ @@ -378,25 +384,11 @@ def _update(self, args, out, name=None, db=None, collection=None): """ conn = self._get_connection(name) if conn is None: - out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') + out('{"ok": 0, "errmsg": "couldn\'t get connection to mongo"}') return if db is None or collection is None: - out('{"ok" : 0, "errmsg" : "db and collection must be defined"}') - return - - if "criteria" not in args: - out('{"ok" : 0, "errmsg" : "missing criteria"}') - return - criteria = self._get_son(args['criteria'], out) - if criteria is None: - return - - if "newobj" not in args: - out('{"ok" : 0, "errmsg" : "missing newobj"}') - return - newobj = self._get_son(args['newobj'], out) - if None == newobj: + out('{"ok": 0, "errmsg": "db and collection must be defined"}') return upsert = False @@ -407,26 +399,60 @@ def _update(self, args, out, name=None, db=None, collection=None): if "multi" in args: multi = bool(args['multi']) - conn[db][collection].update(criteria, newobj, upsert=upsert, multi=multi) + newobj = None + if "newobj" in args: + newobj = self._get_json(args['newobj'], out) + if newobj is None: + out('{"ok": 0, "errmsg": "missing newobj"}') + return + + collection = conn[db][collection] + + criteria = None + if "criteria" not in args: + criteria = self._get_json(args['criteria'], out) + if upsert and not multi and criteria is None: + # Get the criteria from the object and indices + ii = collection.index_information() + criteria = {} + for (ik, o) in ii.items(): + if ik == '_id_' or 'unique' in o and o['unique']: + key = o['key'] + for k in dict(key): + if k in newobj: + criteria[k] = newobj[k] + + if not criteria: + out('{"ok": 0, "errmsg": "missing criteria"}') + return + + collection.update(criteria, newobj, upsert=upsert, multi=multi) self.__safety_check(args, out, conn[db]) + def _insert_or_update(self, args, out, name=None, db=None, collection=None): + """ + insert or update a document in the DB + """ + args['upsert'] = 1 + self._update(args, out, name, db, collection) + def _remove(self, args, out, name=None, db=None, collection=None): """ remove docs """ conn = self._get_connection(name) if conn is None: - out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') + out('{"ok": 0, "errmsg": "couldn\'t get connection to mongo"}') return if db is None or collection is None: - out('{"ok" : 0, "errmsg" : "db and collection must be defined"}') + out('{"ok": 0, "errmsg": "db and collection must be defined"}') return criteria = {} if "criteria" in args: - criteria = self._get_son(args['criteria'], out) + criteria = self._get_json(args['criteria'], out) if criteria is None: return @@ -439,7 +465,7 @@ def _batch(self, args, out, name=None, db=None, collection=None): """ batch process commands """ - requests = self._get_son(args['requests'], out) + requests = self._get_json(args['requests'], out) if requests is None: return @@ -484,6 +510,36 @@ def _batch(self, args, out, name=None, db=None, collection=None): out("]") + def _ensure_index(self, args, out, name=None, db=None, collection=None): + """ + Ensure if the collection has index, if not it will be created, if yes, the result will be cached + """ + conn = self._get_connection(name) + if conn is None: + out('{"ok": 0, "errmsg": "couldn\'t get connection to mongo"}') + return + + if db is None or collection is None: + out('{"ok": 0, "errmsg": "db and collection must be defined"}') + return + + keys = None + if "keys" in args: + keys = self._get_json(args['keys'], out) + if keys is None: + out('{"ok": 0, "errmsg": "missing keys"}') + return + + options = {} + if "options" in args: + options = self._get_json(args['options'], out) + + try: + name = conn[db][collection].ensure_index(keys.items(), **options) + out('{"ok": 1, "name": "%s"}' % name) + except Exception, e: + out('{"ok": 0, "errmsg": "%s"}' % esc(e.message)) + class MongoFakeStream: def __init__(self): diff --git a/sleepymongoose/httpd.py b/sleepymongoose/httpd.py index 076dd04..61ef339 100644 --- a/sleepymongoose/httpd.py +++ b/sleepymongoose/httpd.py @@ -1,4 +1,4 @@ -# Copyright 2009-2010 10gen, Inc. +# Copyright 2009-2015 10gen, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -11,6 +11,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Modified by Adam Wallner - Bitbaro Mobile kft from SocketServer import BaseServer from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer @@ -156,8 +158,6 @@ def process_uri(self, method): # Convert to args for k in pargs.keys(): args[k] = pargs.getvalue(k) - - print args else: self.send_response(100, "Continue") self.send_header('Content-type', MongoHTTPRequest.mimetypes['json']) From 31d5edbcafef27f8418f30f64b274abd9712ee44 Mon Sep 17 00:00:00 2001 From: Adam Wallner Date: Tue, 24 Feb 2015 12:58:35 +0100 Subject: [PATCH 8/8] - errormsg -> err, to be compatible with mongo status - removed safe argument -> update, remove, insert will directly return with mogo's last_status, this is the proper error handling - handle $set, $setOnInsert, etc. properly - small bugs fixed - new gitignore --- .gitignore | 4 +- sleepymongoose/handlers.py | 122 ++++++++++++++++++------------------- 2 files changed, 63 insertions(+), 63 deletions(-) diff --git a/.gitignore b/.gitignore index d258d03..2dc62f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *~ *.log -.idea \ No newline at end of file +.idea +*.pyc +*.pyo \ No newline at end of file diff --git a/sleepymongoose/handlers.py b/sleepymongoose/handlers.py index 5edb979..002f691 100644 --- a/sleepymongoose/handlers.py +++ b/sleepymongoose/handlers.py @@ -103,11 +103,12 @@ def _get_json(self, s, out): try: obj = json.loads(s, object_hook=json_util.object_hook) except (ValueError, TypeError): - out('{"ok": 0, "errmsg": "couldn\'t parse json: %s"}' % esc(s)) + print "Error: Couldn't parse JSON:", s + out('{"ok": 0, "err": "couldn\'t parse JSON: %s"}' % esc(s)) return None if not getattr(obj, '__iter__', False): - out('{"ok": 0, "errmsg": "type is not iterable: %s"}' % s) + out('{"ok": 0, "err": "type is not iterable: %s"}' % s) return None return obj @@ -119,7 +120,7 @@ def _cmd(self, args, out, name=None, db=None, collection=None): conn = self._get_connection(name) if conn is None: - out('{"ok": 0, "errmsg": "couldn\'t get connection to mongo"}') + out('{"ok": 0, "err": "couldn\'t get connection to mongo"}') return cmd = self._get_json(args['cmd'], out) @@ -129,11 +130,11 @@ def _cmd(self, args, out, name=None, db=None, collection=None): try: result = conn[db].command(cmd, check=False) except AutoReconnect: - out('{"ok": 0, "errmsg": "wasn\'t connected to the db and ' + + out('{"ok": 0, "err": "wasn\'t connected to the db and ' + 'couldn\'t reconnect", "name": "%s"}' % name) return except (OperationFailure, error): - out('{"ok": 0, "errmsg": "%s"}' % error) + out('{"ok": 0, "err": "%s"}' % error) return # debugging @@ -168,7 +169,7 @@ def _connect(self, args, out, name=None, db=None, collection=None): uri = args['server'] except Exception, e: print e - out('{"ok": 0, "errmsg": "invalid server uri given"}') + out('{"ok": 0, "err": "invalid server uri given"}') return else: uri = 'mongodb://localhost:27017' @@ -180,7 +181,7 @@ def _connect(self, args, out, name=None, db=None, collection=None): if conn is not None: out('{"ok": 1, "server": "%s", "name": "%s"}' % (uri, name)) else: - out('{"ok": 0, "errmsg": "could not connect", "server": "%s", "name": "%s"}' % (uri, name)) + out('{"ok": 0, "err": "could not connect", "server": "%s", "name": "%s"}' % (uri, name)) # noinspection PyUnusedLocal def _authenticate(self, args, out, name=None, db=None, collection=None): @@ -189,21 +190,21 @@ def _authenticate(self, args, out, name=None, db=None, collection=None): """ conn = self._get_connection(name) if conn is None: - out('{"ok": 0, "errmsg": "couldn\'t get connection to mongo"}') + out('{"ok": 0, "err": "couldn\'t get connection to mongo"}') return if db is None: - out('{"ok": 0, "errmsg": "db must be defined"}') + out('{"ok": 0, "err": "db must be defined"}') return if 'username' not in args: - out('{"ok": 0, "errmsg": "username must be defined"}') + out('{"ok": 0, "err": "username must be defined"}') if 'password' not in args: - out('{"ok": 0, "errmsg": "password must be defined"}') + out('{"ok": 0, "err": "password must be defined"}') if not conn[db].authenticate(args['username'], args['password']): - out('{"ok": 0, "errmsg": "authentication failed"}') + out('{"ok": 0, "err": "authentication failed"}') else: out('{"ok": 1}') @@ -214,7 +215,7 @@ def _disconnect(self, args, out, name=None, db=None, collection=None): """ conn = self._get_connection(name) if conn is None: - out('{"ok": 0, "errmsg": "couldn\'t get connection to mongo"}') + out('{"ok": 0, "err": "couldn\'t get connection to mongo"}') return conn.disconnect() @@ -228,11 +229,11 @@ def _find(self, args, out, name=None, db=None, collection=None): """ conn = self._get_connection(name) if conn is None: - out('{"ok": 0, "errmsg": "couldn\'t get connection to mongo"}') + out('{"ok": 0, "err": "couldn\'t get connection to mongo"}') return if db is None or collection is None: - out('{"ok": 0, "errmsg": "db and collection must be defined"}') + out('{"ok": 0, "err": "db and collection must be defined"}') return criteria = {} @@ -297,14 +298,14 @@ def _more(self, args, out, name=None, db=None, collection=None): Get more results from a cursor """ if 'id' not in args: - out('{"ok": 0, "errmsg": "no cursor id given"}') + out('{"ok": 0, "err": "no cursor id given"}') return _id = int(args["id"][0]) cursors = getattr(self, "cursors") if _id not in cursors: - out('{"ok": 0, "errmsg": "couldn\'t find the cursor with id %d"}' % _id) + out('{"ok": 0, "err": "couldn\'t find the cursor with id %d"}' % _id) return cursor = cursors[_id] @@ -325,10 +326,10 @@ def __output_results(self, cursor, out, batch_size=15): while len(batch) < batch_size: batch.append(cursor.next()) except AutoReconnect: - out(json.dumps({"ok": 0, "errmsg": "auto reconnecting, please try again"})) + out(json.dumps({"ok": 0, "err": "auto reconnecting, please try again"})) return except OperationFailure, of: - out(json.dumps({"ok": 0, "errmsg": "%s" % of})) + out(json.dumps({"ok": 0, "err": "%s" % of})) return except StopIteration: # this is so stupid, there's no has_next? @@ -342,41 +343,26 @@ def _insert(self, args, out, name=None, db=None, collection=None): """ conn = self._get_connection(name) if conn is None: - out('{"ok": 0, "errmsg": "couldn\'t get connection to mongo"}') + out('{"ok": 0, "err": "couldn\'t get connection to mongo"}') return if db is None or collection is None: - out('{"ok": 0, "errmsg": "db and collection must be defined"}') + out('{"ok": 0, "err": "db and collection must be defined"}') return if "docs" not in args: - out('{"ok": 0, "errmsg": "missing docs"}') + out('{"ok": 0, "err": "missing docs"}') return docs = self._get_json(args['docs'], out) if docs is None: return - safe = False - if "safe" in args: - safe = bool(args['safe']) - - result = {'oids': conn[db][collection].insert(docs)} - if safe: - result['status'] = conn[db].last_status() - - out(json.dumps(result, default=json_util.default)) - - def __safety_check(self, args, out, db): - safe = False - if "safe" in args: - safe = bool(args['safe']) - - if safe: - result = db.last_status() - out(json.dumps(result, default=json_util.default)) - else: - out('{"ok": 1}') + try: + conn[db][collection].insert(docs) + out(json.dumps(conn[db].last_status(), default=json_util.default)) + except Exception, e: + out('{"ok": 0, "err": "%s"}' % esc(e.message)) def _update(self, args, out, name=None, db=None, collection=None): """ @@ -384,11 +370,11 @@ def _update(self, args, out, name=None, db=None, collection=None): """ conn = self._get_connection(name) if conn is None: - out('{"ok": 0, "errmsg": "couldn\'t get connection to mongo"}') + out('{"ok": 0, "err": "couldn\'t get connection to mongo"}') return if db is None or collection is None: - out('{"ok": 0, "errmsg": "db and collection must be defined"}') + out('{"ok": 0, "err": "db and collection must be defined"}') return upsert = False @@ -401,15 +387,18 @@ def _update(self, args, out, name=None, db=None, collection=None): newobj = None if "newobj" in args: - newobj = self._get_json(args['newobj'], out) + newobj = args['newobj'] + if newobj is None: + out('{"ok": 0, "err": "missing newobj"}') + return + newobj = self._get_json(newobj, out) if newobj is None: - out('{"ok": 0, "errmsg": "missing newobj"}') return collection = conn[db][collection] criteria = None - if "criteria" not in args: + if "criteria" in args: criteria = self._get_json(args['criteria'], out) if upsert and not multi and criteria is None: # Get the criteria from the object and indices @@ -421,14 +410,18 @@ def _update(self, args, out, name=None, db=None, collection=None): for k in dict(key): if k in newobj: criteria[k] = newobj[k] + elif '$set' in newobj and k in newobj['$set']: + criteria[k] = newobj['$set'][k] if not criteria: - out('{"ok": 0, "errmsg": "missing criteria"}') + out('{"ok": 0, "err": "missing criteria"}') return - collection.update(criteria, newobj, upsert=upsert, multi=multi) - - self.__safety_check(args, out, conn[db]) + try: + collection.update(criteria, newobj, upsert=upsert, multi=multi, check_keys=False) + out(json.dumps(conn[db].last_status(), default=json_util.default)) + except Exception, e: + out('{"ok": 0, "err": "%s"}' % esc(e.message)) def _insert_or_update(self, args, out, name=None, db=None, collection=None): """ @@ -443,11 +436,11 @@ def _remove(self, args, out, name=None, db=None, collection=None): """ conn = self._get_connection(name) if conn is None: - out('{"ok": 0, "errmsg": "couldn\'t get connection to mongo"}') + out('{"ok": 0, "err": "couldn\'t get connection to mongo"}') return if db is None or collection is None: - out('{"ok": 0, "errmsg": "db and collection must be defined"}') + out('{"ok": 0, "err": "db and collection must be defined"}') return criteria = {} @@ -455,10 +448,11 @@ def _remove(self, args, out, name=None, db=None, collection=None): criteria = self._get_json(args['criteria'], out) if criteria is None: return - - conn[db][collection].remove(criteria) - - self.__safety_check(args, out, conn[db]) + try: + conn[db][collection].remove(criteria) + out(json.dumps(conn[db].last_status())) + except Exception, e: + out('{"ok": 0, "err": "%s"}' % esc(e.message)) # noinspection PyUnusedLocal def _batch(self, args, out, name=None, db=None, collection=None): @@ -516,29 +510,33 @@ def _ensure_index(self, args, out, name=None, db=None, collection=None): """ conn = self._get_connection(name) if conn is None: - out('{"ok": 0, "errmsg": "couldn\'t get connection to mongo"}') + out('{"ok": 0, "err": "couldn\'t get connection to mongo"}') return if db is None or collection is None: - out('{"ok": 0, "errmsg": "db and collection must be defined"}') + out('{"ok": 0, "err": "db and collection must be defined"}') return keys = None if "keys" in args: keys = self._get_json(args['keys'], out) if keys is None: - out('{"ok": 0, "errmsg": "missing keys"}') + out('{"ok": 0, "err": "missing keys"}') return options = {} if "options" in args: options = self._get_json(args['options'], out) + cache_for = 10 + if "cache_for" in args: + cache_for = args['cache_for'] + try: - name = conn[db][collection].ensure_index(keys.items(), **options) + name = conn[db][collection].ensure_index(keys.items(), cache_for=cache_for, **options) out('{"ok": 1, "name": "%s"}' % name) except Exception, e: - out('{"ok": 0, "errmsg": "%s"}' % esc(e.message)) + out('{"ok": 0, "err": "%s"}' % esc(e.message)) class MongoFakeStream: