From 0aa9af3dfc6460291bbb37f5f39408cacd09be61 Mon Sep 17 00:00:00 2001 From: David Glasser Date: Mon, 17 Mar 2014 11:10:06 -0700 Subject: [PATCH 01/11] Make some white-box tests less flaky If we want the *internal* state of the oplog driver to be consistent, we need to make sure that we start our observes at a consistent point in the oplog. (ie, initial inserts need to have been fully processed, so that we don't process them during the original QUERYING phase, which can give us a different unpublished buffer.) Note that only the white-box tests (looked at _unpublishedBuffer, eg) appeared to be flaky: the actual docs published seemed to be correct in all cases. --- .../mongo-livedata/mongo_livedata_tests.js | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/mongo-livedata/mongo_livedata_tests.js b/packages/mongo-livedata/mongo_livedata_tests.js index 48dfa6fa92..20f2126886 100644 --- a/packages/mongo-livedata/mongo_livedata_tests.js +++ b/packages/mongo-livedata/mongo_livedata_tests.js @@ -818,6 +818,8 @@ if (Meteor.isServer) { // Insert a doc and start observing. var docId1 = ins({foo: 22, bar: 5}); + waitUntilOplogCaughtUp(); + // State: [ 5:1 | ]! var o = observer(); var usesOplog = o.handle._multiplexer._observeDriver._usesOplog; @@ -1154,6 +1156,10 @@ if (Meteor.isServer) { ids[i] = ins({ x: x, y: i }); }); + // Ensure that we are past all the 'i' entries before we run the query, so + // that we get the expected phase transitions. + waitUntilOplogCaughtUp(); + var o = observer(); var usesOplog = o.handle._multiplexer._observeDriver._usesOplog; // x: [1 1 2 | 2 3 3] 4 5 5 5 9 @@ -2407,8 +2413,7 @@ Meteor.isServer && Tinytest.add("mongo-livedata - oplog - include selector field // during the observeChanges, the bug in question is not consistently // reproduced.) We don't have to do this for polling observe (eg // --disable-oplog). - var oplog = MongoInternals.defaultRemoteCollectionDriver().mongo._oplogHandle; - oplog && oplog.waitUntilCaughtUp(); + waitUntilOplogCaughtUp(); var output = []; var handle = coll.find({a: 1, b: 2}, {fields: {c: 1}}).observeChanges({ @@ -2450,8 +2455,7 @@ Meteor.isServer && Tinytest.add("mongo-livedata - oplog - transform", function ( // during the observeChanges, the bug in question is not consistently // reproduced.) We don't have to do this for polling observe (eg // --disable-oplog). - var oplog = MongoInternals.defaultRemoteCollectionDriver().mongo._oplogHandle; - oplog && oplog.waitUntilCaughtUp(); + waitUntilOplogCaughtUp(); var cursor = coll.find({}, {transform: function (doc) { return doc.x; @@ -2513,8 +2517,7 @@ Meteor.isServer && Tinytest.add("mongo-livedata - oplog - drop collection", func // Wait until we've processed the insert oplog entry, so that we are in a // steady state (and we don't see the dropped docs because we are FETCHING). - var oplog = MongoInternals.defaultRemoteCollectionDriver().mongo._oplogHandle; - oplog && oplog.waitUntilCaughtUp(); + waitUntilOplogCaughtUp(); // Drop the collection. Should remove all docs. runInFence(function () { @@ -2664,3 +2667,12 @@ testAsyncMulti("mongo-livedata - oplog - update EJSON", [ self.handle.stop(); } ]); + + +var waitUntilOplogCaughtUp = function () { + var oplogHandle = + MongoInternals.defaultRemoteCollectionDriver().mongo._oplogHandle; + if (oplogHandle) + oplogHandle.waitUntilCaughtUp(); +}; + From 0a2b213682ccf3f2641316e44088ec4a99062698 Mon Sep 17 00:00:00 2001 From: David Glasser Date: Mon, 17 Mar 2014 11:35:29 -0700 Subject: [PATCH 02/11] Ensure that hook.each is called from a Fiber That gives us enough guarantees that we can simplify the error-handling code. We also ensure that the "new connection" handler in livedata_server runs in a Fiber, which fixes a bug introduced in d049bf7506 where connections from pre-pre1 clients would crash (due to Meteor.setTimeout which only works in a Fiber). --- packages/callback-hook/hook.js | 25 ++++++++++--------------- packages/livedata/livedata_server.js | 4 +++- packages/meteor/dynamics_browser.js | 6 ++++++ packages/meteor/dynamics_nodejs.js | 22 +++++++++++++--------- 4 files changed, 32 insertions(+), 25 deletions(-) diff --git a/packages/callback-hook/hook.js b/packages/callback-hook/hook.js index 0cd4ac394b..43c493904e 100644 --- a/packages/callback-hook/hook.js +++ b/packages/callback-hook/hook.js @@ -57,7 +57,10 @@ _.extend(Hook.prototype, { callback = Meteor.bindEnvironment( callback, self.exceptionHandler || function (exception) { - self.exception = exception; + // Note: this relies on the undocumented fact that if bindEnvironment's + // onException throws, and you are invoking the callback either in the + // browser or from within a Fiber in Node, the exception is propagated. + throw exception; } ); @@ -83,6 +86,12 @@ _.extend(Hook.prototype, { // each: function (iterator) { var self = this; + + // Invoking bindEnvironment'd callbacks outside of a Fiber in Node doesn't + // run them to completion (and exceptions thrown from onException are not + // propagated), so we need to be in a Fiber. + Meteor._nodeCodeMustBeInFiber(); + var ids = _.keys(self.callbacks); for (var i = 0; i < ids.length; ++i) { var id = ids[i]; @@ -90,20 +99,6 @@ _.extend(Hook.prototype, { if (_.has(self.callbacks, id)) { var callback = self.callbacks[id]; - if (! self.exceptionHandler) { - var originalCallback = callback; - callback = function (/*arguments*/) { - self.exception = null; - var ret = originalCallback.apply(null, arguments); - if (self.exception) { - var exception = self.exception; - self.exception = null; - throw exception; - } - return ret; - }; - } - if (! iterator(callback)) break; } diff --git a/packages/livedata/livedata_server.js b/packages/livedata/livedata_server.js index 1faf63f751..cbdd29ae63 100644 --- a/packages/livedata/livedata_server.js +++ b/packages/livedata/livedata_server.js @@ -1099,7 +1099,9 @@ Server = function () { sendError("Already connected", msg); return; } - self._handleConnect(socket, msg); + Fiber(function () { + self._handleConnect(socket, msg); + }).run(); return; } diff --git a/packages/meteor/dynamics_browser.js b/packages/meteor/dynamics_browser.js index 01ebcaf941..06c6860612 100644 --- a/packages/meteor/dynamics_browser.js +++ b/packages/meteor/dynamics_browser.js @@ -46,6 +46,8 @@ Meteor.bindEnvironment = function (func, onException, _this) { currentValues = boundValues; var ret = func.apply(_this, _.toArray(arguments)); } catch (e) { + // note: callback-hook currently relies on the fact that if onException + // throws in the browser, the wrapped call throws. onException(e); } finally { currentValues = savedValues; @@ -53,3 +55,7 @@ Meteor.bindEnvironment = function (func, onException, _this) { return ret; }; }; + +Meteor._nodeCodeMustBeInFiber = function () { + // no-op on browser +}; diff --git a/packages/meteor/dynamics_nodejs.js b/packages/meteor/dynamics_nodejs.js index 4d19eb9422..6dc2add875 100644 --- a/packages/meteor/dynamics_nodejs.js +++ b/packages/meteor/dynamics_nodejs.js @@ -4,9 +4,13 @@ var Fiber = Npm.require('fibers'); var nextSlot = 0; -var noFiberMessage = "Meteor code must always run within a Fiber. " + - "Try wrapping callbacks that you pass to non-Meteor " + - "libraries with Meteor.bindEnvironment."; +Meteor._nodeCodeMustBeInFiber = function () { + if (!Fiber.current) { + throw new Error("Meteor code must always run within a Fiber. " + + "Try wrapping callbacks that you pass to non-Meteor " + + "libraries with Meteor.bindEnvironment."); + } +}; Meteor.EnvironmentVariable = function () { this.slot = nextSlot++; @@ -14,16 +18,14 @@ Meteor.EnvironmentVariable = function () { _.extend(Meteor.EnvironmentVariable.prototype, { get: function () { - if (!Fiber.current) - throw new Error(noFiberMessage); + Meteor._nodeCodeMustBeInFiber(); return Fiber.current._meteor_dynamics && Fiber.current._meteor_dynamics[this.slot]; }, withValue: function (value, func) { - if (!Fiber.current) - throw new Error(noFiberMessage); + Meteor._nodeCodeMustBeInFiber(); if (!Fiber.current._meteor_dynamics) Fiber.current._meteor_dynamics = []; @@ -61,8 +63,7 @@ _.extend(Meteor.EnvironmentVariable.prototype, { // callback, and when an exception is raised a debug message will be // printed with the description. Meteor.bindEnvironment = function (func, onException, _this) { - if (!Fiber.current) - throw new Error(noFiberMessage); + Meteor._nodeCodeMustBeInFiber(); var boundValues = _.clone(Fiber.current._meteor_dynamics || []); @@ -87,6 +88,9 @@ Meteor.bindEnvironment = function (func, onException, _this) { Fiber.current._meteor_dynamics = _.clone(boundValues); var ret = func.apply(_this, args); } catch (e) { + // note: callback-hook currently relies on the fact that if onException + // throws and you were originally calling the wrapped callback from + // within a Fiber, the wrapped call throws. onException(e); } finally { Fiber.current._meteor_dynamics = savedValues; From 3da9c7db897594abf382919de34139b7cf723e2c Mon Sep 17 00:00:00 2001 From: Nick Martin Date: Tue, 18 Mar 2014 00:59:03 -0700 Subject: [PATCH 03/11] version bump examples and docs. --- docs/.meteor/release | 2 +- docs/lib/release-override.js | 2 +- examples/leaderboard/.meteor/release | 2 +- examples/parties/.meteor/release | 2 +- examples/todos/.meteor/release | 2 +- examples/wordplay/.meteor/release | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/.meteor/release b/docs/.meteor/release index 5a848a1d77..f4ca42eed8 100644 --- a/docs/.meteor/release +++ b/docs/.meteor/release @@ -1 +1 @@ -0.7.1.2 +0.7.2-rc1 diff --git a/docs/lib/release-override.js b/docs/lib/release-override.js index 523bf17b11..b99159cbf3 100644 --- a/docs/lib/release-override.js +++ b/docs/lib/release-override.js @@ -1,5 +1,5 @@ // While galaxy apps are on their own special meteor releases, override // Meteor.release here. if (Meteor.isClient) { - Meteor.release = Meteor.release ? "0.7.1.2" : undefined; + Meteor.release = Meteor.release ? "0.7.2" : undefined; } diff --git a/examples/leaderboard/.meteor/release b/examples/leaderboard/.meteor/release index 5a848a1d77..f4ca42eed8 100644 --- a/examples/leaderboard/.meteor/release +++ b/examples/leaderboard/.meteor/release @@ -1 +1 @@ -0.7.1.2 +0.7.2-rc1 diff --git a/examples/parties/.meteor/release b/examples/parties/.meteor/release index 5a848a1d77..f4ca42eed8 100644 --- a/examples/parties/.meteor/release +++ b/examples/parties/.meteor/release @@ -1 +1 @@ -0.7.1.2 +0.7.2-rc1 diff --git a/examples/todos/.meteor/release b/examples/todos/.meteor/release index 5a848a1d77..f4ca42eed8 100644 --- a/examples/todos/.meteor/release +++ b/examples/todos/.meteor/release @@ -1 +1 @@ -0.7.1.2 +0.7.2-rc1 diff --git a/examples/wordplay/.meteor/release b/examples/wordplay/.meteor/release index 5a848a1d77..f4ca42eed8 100644 --- a/examples/wordplay/.meteor/release +++ b/examples/wordplay/.meteor/release @@ -1 +1 @@ -0.7.1.2 +0.7.2-rc1 From 668f23f548ddc08dfe8af9f621ab9e8c43c4efea Mon Sep 17 00:00:00 2001 From: Nick Martin Date: Tue, 18 Mar 2014 01:06:10 -0700 Subject: [PATCH 04/11] first cut banner and notices. --- scripts/admin/banner.txt | 5 +++-- scripts/admin/notices.json | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/admin/banner.txt b/scripts/admin/banner.txt index e672e9ae21..02772f1e45 100644 --- a/scripts/admin/banner.txt +++ b/scripts/admin/banner.txt @@ -1,4 +1,5 @@ -=> Meteor 0.7.1.2: Fix crash on OSX machines with no hostname set. +=> Meteor 0.7.2: Support the limit option in the oplog tailing driver. + Add hooks to customize the login process. This release is being downloaded in the background. Update your - project to Meteor 0.7.1.2 by running 'meteor update'. + project to Meteor 0.7.2 by running 'meteor update'. diff --git a/scripts/admin/notices.json b/scripts/admin/notices.json index 835f91f1f8..f63180f934 100644 --- a/scripts/admin/notices.json +++ b/scripts/admin/notices.json @@ -91,6 +91,9 @@ { "release": "0.7.1.2" }, + { + "release": "0.7.2" + }, { "release": "NEXT" } From d97409a17dd86712f04c5a84a69df07a6a15a551 Mon Sep 17 00:00:00 2001 From: Nick Martin Date: Tue, 18 Mar 2014 01:22:43 -0700 Subject: [PATCH 05/11] history tweak and version bump. --- History.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index 457e2cdfaf..5920acbac6 100644 --- a/History.md +++ b/History.md @@ -1,12 +1,16 @@ ## v.NEXT +## v0.7.2 + * Support oplog tailing on queries with the `limit` option. All queries except those containing `$near` or `$where` selectors or the `skip` option can now be used with the oplog driver. -* Add hooks to login process. This allows for rate limiting login - attempts, logging an audit trail, account lockout flags, etc. See: - http://docs.meteor.com/#accounts_validLoginAttempt #1815 +* Add hooks to login process: `Accounts.onLogin`, + `Accounts.onLoginFailure`, and `Accounts.validateLoginAttempt`. These + functions allow for rate limiting login attempts, logging an audit + trail, account lockout flags, and more. See: + http://docs.meteor.com/#accounts_validateloginattempt #1815 * Change the `Accounts.registerLoginHandler` API for custom login methods. Login handlers now require a name and no longer have to deal From 79ad5437c70c1d1930793e534dc664ac287914fe Mon Sep 17 00:00:00 2001 From: Nick Martin Date: Tue, 18 Mar 2014 01:28:10 -0700 Subject: [PATCH 06/11] Note that connection is also passed to login hooks. --- docs/client/api.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/client/api.html b/docs/client/api.html index bc752a7dc1..5de1cb7a2f 100644 --- a/docs/client/api.html +++ b/docs/client/api.html @@ -1823,6 +1823,11 @@ are called with a single argument, the attempt info object: object. This will always be present for successful logins. {{/dtdd}} +{{#dtdd name="connection" type="Object"}} + The `connection` object the request came in on. See + [`Meteor.onConnection`](#meteor_onconnection) for details. +{{/dtdd}} + {{#dtdd name="methodName" type="String"}} The name of the Meteor method being used to login. {{/dtdd}} From d3295f1f410cf3b51ef58a2eb34b5afb40955e02 Mon Sep 17 00:00:00 2001 From: Nick Martin Date: Tue, 18 Mar 2014 13:46:24 -0700 Subject: [PATCH 07/11] combine onLogin and onLoginFailure in docs. --- docs/client/api.html | 15 +++++---------- docs/client/api.js | 20 +++----------------- docs/client/docs.js | 2 +- 3 files changed, 9 insertions(+), 28 deletions(-) diff --git a/docs/client/api.html b/docs/client/api.html index 5de1cb7a2f..0957940147 100644 --- a/docs/client/api.html +++ b/docs/client/api.html @@ -1850,23 +1850,18 @@ successful. {{> api_box accounts_onLogin}} -Call `onLogin` or `onLoginFailure` with a callback to be called when a -login is successful or is unsuccessful. These functions return an -object with a single method, `stop`. Calling `stop()` unregisters the -callback. - Either the `onLogin` or the `onLoginFailure` callbacks will be called for each login attempt. The `onLogin` callbacks are called after the -user has been logged in. +user has been successfully logged in. The `onLoginFailure` callbacks are +called after a login attempt is denied. + +These functions return an object with a single method, `stop`. Calling +`stop()` unregisters the callback. The callbacks are called with a single argument, the same attempt info object as [`validateLoginAttempt`](#accounts_validateloginattempt). -{{> api_box accounts_onLoginFailure}} - - -

Passwords

The `accounts-password` package contains a full system for password-based diff --git a/docs/client/api.js b/docs/client/api.js index 830b610df0..ab028551c9 100644 --- a/docs/client/api.js +++ b/docs/client/api.js @@ -1273,28 +1273,14 @@ Template.api.accounts_validateLoginAttempt = { Template.api.accounts_onLogin = { id: "accounts_onlogin", - name: "Accounts.onLogin(func)", + name: "Accounts.onLogin(func) and Accounts.onLoginFailure(func)", locus: "Server", - descr: ["Register a callback to be called after a login is successful."], + descr: ["Register a callback to be called after a login attempt."], args: [ { name: "func", type: "Function", - descr: "The callback to be called after a login is successful." - } - ] -}; - -Template.api.accounts_onLoginFailure = { - id: "accounts_onloginfailure", - name: "Accounts.onLoginFailure(func)", - locus: "Server", - descr: ["Register a callback to be called when a login is attempted and is unsuccessful. See [`Accounts.onLogin`](#accounts_onlogin) for details."], - args: [ - { - name: "func", - type: "Function", - descr: "The callback to be called after an unsuccessful login." + descr: "The callback to be called after the login attempt" } ] }; diff --git a/docs/client/docs.js b/docs/client/docs.js index 17c599291e..75d09ea0c5 100644 --- a/docs/client/docs.js +++ b/docs/client/docs.js @@ -214,7 +214,7 @@ var toc = [ "Accounts.onCreateUser", "Accounts.validateLoginAttempt", "Accounts.onLogin", - "Accounts.onLoginFailure" + {name: "Accounts.onLoginFailure", id: "accounts_onlogin"}, ], {name: "Passwords", id: "accounts_passwords"}, [ From 294a945e17720925f94be17ea99e197ba3b64da2 Mon Sep 17 00:00:00 2001 From: Nick Martin Date: Tue, 18 Mar 2014 14:02:22 -0700 Subject: [PATCH 08/11] update banner text --- scripts/admin/banner.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/admin/banner.txt b/scripts/admin/banner.txt index 02772f1e45..571b063d08 100644 --- a/scripts/admin/banner.txt +++ b/scripts/admin/banner.txt @@ -1,5 +1,5 @@ -=> Meteor 0.7.2: Support the limit option in the oplog tailing driver. - Add hooks to customize the login process. +=> Meteor 0.7.2: Support limit queries in the oplog tailing driver. Add + hooks to customize the login process. This release is being downloaded in the background. Update your project to Meteor 0.7.2 by running 'meteor update'. From 79551f0aa6e981732b988347961aeed6fcd86123 Mon Sep 17 00:00:00 2001 From: David Glasser Date: Tue, 18 Mar 2014 14:08:08 -0700 Subject: [PATCH 09/11] Document relationship between two validate hooks --- docs/client/api.html | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/client/api.html b/docs/client/api.html index 0957940147..62991b5167 100644 --- a/docs/client/api.html +++ b/docs/client/api.html @@ -1752,6 +1752,14 @@ Example: return user.username !== "root"; }); +If the user is being created as part of a login attempt from a client (eg, +calling [`Accounts.createUser`](#accounts_createuser) from the client, or +[logging in for the first time with an external +service](#meteor_loginwithexternalservice)), these callbacks are called *before* +the [`Accounts.validateLoginAttempt`](#accounts_validateloginattempt) +callbacks. If these callbacks succeed but those fail, the user will still be +created but the connection will not be logged in as that user. + {{> api_box accounts_onCreateUser}} Use this when you need to do more than simply accept or reject new user From 398d543cdd55899ef2c53b381f0c9be6b434bd5f Mon Sep 17 00:00:00 2001 From: Nick Martin Date: Tue, 18 Mar 2014 14:12:38 -0700 Subject: [PATCH 10/11] update docs to real release. --- docs/.meteor/release | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/.meteor/release b/docs/.meteor/release index f4ca42eed8..7486fdbc50 100644 --- a/docs/.meteor/release +++ b/docs/.meteor/release @@ -1 +1 @@ -0.7.2-rc1 +0.7.2 From e699743d6d56bf8346bb4043a5cc5642394da5fb Mon Sep 17 00:00:00 2001 From: Nick Martin Date: Tue, 18 Mar 2014 14:13:48 -0700 Subject: [PATCH 11/11] bump version of examples. --- examples/leaderboard/.meteor/release | 2 +- examples/parties/.meteor/release | 2 +- examples/todos/.meteor/release | 2 +- examples/wordplay/.meteor/release | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/leaderboard/.meteor/release b/examples/leaderboard/.meteor/release index f4ca42eed8..7486fdbc50 100644 --- a/examples/leaderboard/.meteor/release +++ b/examples/leaderboard/.meteor/release @@ -1 +1 @@ -0.7.2-rc1 +0.7.2 diff --git a/examples/parties/.meteor/release b/examples/parties/.meteor/release index f4ca42eed8..7486fdbc50 100644 --- a/examples/parties/.meteor/release +++ b/examples/parties/.meteor/release @@ -1 +1 @@ -0.7.2-rc1 +0.7.2 diff --git a/examples/todos/.meteor/release b/examples/todos/.meteor/release index f4ca42eed8..7486fdbc50 100644 --- a/examples/todos/.meteor/release +++ b/examples/todos/.meteor/release @@ -1 +1 @@ -0.7.2-rc1 +0.7.2 diff --git a/examples/wordplay/.meteor/release b/examples/wordplay/.meteor/release index f4ca42eed8..7486fdbc50 100644 --- a/examples/wordplay/.meteor/release +++ b/examples/wordplay/.meteor/release @@ -1 +1 @@ -0.7.2-rc1 +0.7.2