diff --git a/.gitignore b/.gitignore index 518a1c6..2dc62f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ *~ *.log +.idea +*.pyc +*.pyo \ No newline at end of file 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/requirements.txt b/requirements.txt index de4887b..a25b00e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ +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..002f691 100644 --- a/sleepymongoose/handlers.py +++ b/sleepymongoose/handlers.py @@ -11,18 +11,28 @@ # 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 +# noinspection PyPackageRequirements +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 + +def esc(s): + return str(s).replace('"', '\\"') + + class MongoHandler: mh = None @@ -32,26 +42,26 @@ def __init__(self, mongos): self.connections = {} for host in mongos: - args = MongoFakeFieldStorage({"server" : host}) + args = {"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,174 +73,179 @@ 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_json(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) + print "Error: Couldn't parse JSON:", s + out('{"ok": 0, "err": "couldn\'t parse JSON: %s"}' % esc(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, "err": "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: - out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') + if conn is None: + out('{"ok": 0, "err": "couldn\'t get connection to mongo"}') return - cmd = self._get_son(args.getvalue('cmd'), out) - if cmd == None: + 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, "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 if result['ok'] == 0: - result['cmd'] = args.getvalue('cmd') + result['cmd'] = args['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 """ - - 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 uri print e - out('{"ok" : 0, "errmsg" : "invalid server uri given", "server" : "%s"}' % uri) + out('{"ok": 0, "err": "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: - out('{"ok" : 1, "server" : "%s", "name" : "%s"}' % (uri, name)) + 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)) - 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. """ - - if type(args).__name__ == 'dict': - out('{"ok" : 0, "errmsg" : "_find must be a POST request"}') - return - conn = self._get_connection(name) - if conn == None: - out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') + if conn is None: + out('{"ok": 0, "err": "couldn\'t get connection to mongo"}') return - if db == None: - out('{"ok" : 0, "errmsg" : "db must be defined"}') + if db is None: + out('{"ok": 0, "err": "db must be defined"}') return - if not 'username' in args: - out('{"ok" : 0, "errmsg" : "username must be defined"}') + if 'username' not in args: + out('{"ok": 0, "err": "username must be defined"}') + + if 'password' not in args: + out('{"ok": 0, "err": "password must be defined"}') - if not 'password' 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"}') + if not conn[db].authenticate(args['username'], args['password']): + out('{"ok": 0, "err": "authentication failed"}') else: - out('{"ok" : 1}') - - def _find(self, args, out, name = None, db = None, collection = None): + out('{"ok": 1}') + + # noinspection PyUnusedLocal + def _disconnect(self, args, out, name=None, db=None, collection=None): """ - query the database. + disconnect from the database """ - - if type(args).__name__ != 'dict': - out('{"ok" : 0, "errmsg" : "_find must be a GET request"}') + conn = self._get_connection(name) + if conn is None: + out('{"ok": 0, "err": "couldn\'t get connection to mongo"}') return + conn.disconnect() + del self.connections[name] + + out('{"ok": 1}') + + def _find(self, args, out, name=None, db=None, collection=None): + """ + query the database. + """ conn = self._get_connection(name) - if conn == None: - out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') + if conn is None: + out('{"ok": 0, "err": "couldn\'t get connection to mongo"}') return - if db == None or collection == None: - out('{"ok" : 0, "errmsg" : "db and collection must be defined"}') - return + if db is None or collection is None: + out('{"ok": 0, "err": "db and collection must be defined"}') + return criteria = {} if 'criteria' in args: - criteria = self._get_son(args['criteria'][0], out) - if criteria == None: + 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) - if fields == None: + fields = self._get_json(args['fields'][0], out) + if fields is None: return limit = 0 @@ -243,10 +258,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: + sort = self._get_json(args['sort'][0], out) + if sort is None: return stupid_sort = [] @@ -260,48 +274,41 @@ 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 """ - - 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"}') + out('{"ok": 0, "err": "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, "err": "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 +316,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,151 +326,141 @@ 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? 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 """ - - if type(args).__name__ == 'dict': - out('{"ok" : 0, "errmsg" : "_insert must be a POST request"}') - return - conn = self._get_connection(name) - if conn == None: - out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') + if conn is None: + out('{"ok": 0, "err": "couldn\'t get connection to mongo"}') return - if db == None or collection == None: - out('{"ok" : 0, "errmsg" : "db and collection must be defined"}') + if db is None or collection is None: + out('{"ok": 0, "err": "db and collection must be defined"}') return - if "docs" not in args: - out('{"ok" : 0, "errmsg" : "missing docs"}') + if "docs" not in args: + out('{"ok": 0, "err": "missing docs"}') return - docs = self._get_son(args.getvalue('docs'), out) - if docs == None: + docs = self._get_json(args['docs'], out) + if docs is None: return - safe = False - if "safe" in args: - safe = bool(args.getvalue("safe")) - - result = {} - 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.getvalue("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): + 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 == None: - out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') + if conn is None: + out('{"ok": 0, "err": "couldn\'t get connection to mongo"}') return - if db == None or collection == 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.getvalue('criteria'), out) - if criteria == None: + if db is None or collection is None: + out('{"ok": 0, "err": "db and collection must be defined"}') return - if "newobj" not in args: - out('{"ok" : 0, "errmsg" : "missing newobj"}') - return - newobj = self._get_son(args.getvalue('newobj'), out) - if newobj == None: - 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']) + + newobj = None + if "newobj" in args: + newobj = args['newobj'] + if newobj is None: + out('{"ok": 0, "err": "missing newobj"}') + return + newobj = self._get_json(newobj, out) + if newobj is None: + return + + collection = conn[db][collection] - conn[db][collection].update(criteria, newobj, upsert=upsert, multi=multi) + criteria = None + 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 + 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] + elif '$set' in newobj and k in newobj['$set']: + criteria[k] = newobj['$set'][k] + + if not criteria: + out('{"ok": 0, "err": "missing criteria"}') + return - 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 _remove(self, args, out, name = None, db = None, collection = None): + def _insert_or_update(self, args, out, name=None, db=None, collection=None): """ - remove docs + insert or update a document in the DB """ + args['upsert'] = 1 + self._update(args, out, name, db, collection) - if type(args).__name__ == 'dict': - out('{"ok" : 0, "errmsg" : "_remove must be a POST request"}') - return - + def _remove(self, args, out, name=None, db=None, collection=None): + """ + remove docs + """ conn = self._get_connection(name) - if conn == None: - out('{"ok" : 0, "errmsg" : "couldn\'t get connection to mongo"}') + if conn is None: + out('{"ok": 0, "err": "couldn\'t get connection to mongo"}') return - if db == None or collection == None: - out('{"ok" : 0, "errmsg" : "db and collection must be defined"}') + if db is None or collection is None: + out('{"ok": 0, "err": "db and collection must be defined"}') return - + criteria = {} if "criteria" in args: - criteria = self._get_son(args.getvalue('criteria'), out) - if criteria == None: + criteria = self._get_json(args['criteria'], out) + if criteria is None: return - - result = 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)) - 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 """ - - if type(args).__name__ == 'dict': - out('{"ok" : 0, "errmsg" : "_batch must be a POST request"}') - return - - requests = self._get_son(args.getvalue('requests'), out) - if requests == None: + requests = self._get_json(args['requests'], out) + if requests is None: return out("[") @@ -478,7 +474,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'] @@ -494,13 +490,10 @@ 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() - 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 +504,41 @@ 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, "err": "couldn\'t get connection to mongo"}') + return + + if db is None or collection is None: + 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, "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(), cache_for=cache_for, **options) + out('{"ok": 1, "name": "%s"}' % name) + except Exception, e: + out('{"ok": 0, "err": "%s"}' % esc(e.message)) + + class MongoFakeStream: def __init__(self): self.str = "" @@ -521,13 +548,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 53629d5..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,17 +11,22 @@ # 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 + 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 +41,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 +63,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,30 +91,26 @@ 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 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: @@ -117,7 +118,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,29 +128,36 @@ 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": - (uri, q, args) = self.path.partition('?') + # Parse GET parameters + (uri, q, args) = self.path.partition('?') + if len(args): + args = dict(urlparse.parse_qsl(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']}) + 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.getvalue(k) else: self.send_response(100, "Continue") self.send_header('Content-type', MongoHTTPRequest.mimetypes['json']) @@ -158,8 +166,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 +174,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,34 +202,29 @@ def do_GET(self): return else: - self.send_error(404, 'File Not Found: '+uri) + self.send_error(404, 'File Not Found: ' + uri) 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, 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 "| MongoDB HTTP 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 +233,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 +263,58 @@ 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()