/// <summary>
        /// Verifies and registers a facebook token for use in replication authentication
        /// </summary>
        /// <returns>The response state for further HTTP processing</returns>
        /// <param name="context">The context of the Couchbase Lite HTTP request</param>
        public static ICouchbaseResponseState RegisterFacebookToken(ICouchbaseListenerContext context)
        {
            var response = context.CreateResponse();
            var body = context.BodyAs<Dictionary<string, object>>();

            string email = body.GetCast<string>("email");
            string remoteUrl = body.GetCast<string>("remote_url");
            string accessToken = body.GetCast<string>("access_token");
            if (email != null && remoteUrl != null && accessToken != null) {
                Uri siteUrl;
                if (!Uri.TryCreate(remoteUrl, UriKind.Absolute, out siteUrl)) {
                    response.InternalStatus = StatusCode.BadParam;
                    response.JsonBody = new Body(new Dictionary<string, object> {
                        { "error", "invalid remote_url" }
                    });
                } else if (!FacebookAuthorizer.RegisterAccessToken(accessToken, email, siteUrl)) {
                    response.InternalStatus = StatusCode.BadParam;
                    response.JsonBody = new Body(new Dictionary<string, object> {
                        { "error", "invalid access_token" }
                    });
                } else {
                    response.JsonBody = new Body(new Dictionary<string, object> {
                        { "ok", "registered" },
                        { "email", email }
                    });
                }
            } else {
                response.InternalStatus = StatusCode.BadParam;
                response.JsonBody = new Body(new Dictionary<string, object> {
                    { "error", "required fields: access_token, email, remote_url" }
                });
            }

            return response.AsDefaultState();
        }
Beispiel #2
0
        /// <summary>
        /// A database purge permanently removes the references to deleted documents from the database.
        /// </summary>
        /// <returns>The response state for further HTTP processing</returns>
        /// <param name="context">The context of the Couchbase Lite HTTP request</param>
        /// <remarks>
        /// http://docs.couchdb.org/en/latest/api/database/misc.html#post--db-_purge
        /// <remarks>
        public static ICouchbaseResponseState Purge(ICouchbaseListenerContext context)
        {
            return(PerformLogicWithDatabase(context, true, db =>
            {
                var body = context.BodyAs <Dictionary <string, IList <string> > >();
                if (body == null)
                {
                    return context.CreateResponse(StatusCode.BadJson);
                }

                var purgedRevisions = db.Storage.PurgeRevisions(body);
                if (purgedRevisions == null)
                {
                    return context.CreateResponse(StatusCode.DbError);
                }

                var responseBody = new Body(new Dictionary <string, object>
                {
                    { "purged", purgedRevisions }
                });

                var retVal = context.CreateResponse();
                retVal.JsonBody = responseBody;
                return retVal;
            }).AsDefaultState());
        }
Beispiel #3
0
        /// <summary>
        /// The POST to _all_docs allows to specify multiple keys to be selected from the database.
        /// This enables you to request multiple documents in a single request, in place of multiple GET /{db}/{docid} requests.
        /// </summary>
        /// <returns>The response state for further HTTP processing</returns>
        /// <param name="context">The context of the Couchbase Lite HTTP request</param>
        /// <remarks>
        /// http://docs.couchdb.org/en/latest/api/database/bulk-api.html#post--db-_all_docs
        /// <remarks>
        public static ICouchbaseResponseState GetAllSpecifiedDocuments(ICouchbaseListenerContext context)
        {
            return(PerformLogicWithDatabase(context, true, db =>
            {
                var options = context.QueryOptions;
                if (options == null)
                {
                    return context.CreateResponse(StatusCode.BadParam);
                }

                var body = context.BodyAs <Dictionary <string, object> >();
                if (body == null)
                {
                    return context.CreateResponse(StatusCode.BadJson);
                }

                if (!body.ContainsKey("keys"))
                {
                    return context.CreateResponse(StatusCode.BadParam);
                }

                var keys = body["keys"].AsList <object>();
                options.Keys = keys;
                return DoAllDocs(context, db, options);
            }).AsDefaultState());
        }
Beispiel #4
0
        /// <summary>
        /// Verifies and registers a facebook token for use in replication authentication
        /// </summary>
        /// <returns>The response state for further HTTP processing</returns>
        /// <param name="context">The context of the Couchbase Lite HTTP request</param>
        public static ICouchbaseResponseState RegisterFacebookToken(ICouchbaseListenerContext context)
        {
            var response = context.CreateResponse();
            var body     = context.BodyAs <Dictionary <string, object> >();

            string email       = body.GetCast <string>("email");
            string remoteUrl   = body.GetCast <string>("remote_url");
            string accessToken = body.GetCast <string>("access_token");

            if (email != null && remoteUrl != null && accessToken != null)
            {
                Uri siteUrl;
                if (!Uri.TryCreate(remoteUrl, UriKind.Absolute, out siteUrl))
                {
                    response.InternalStatus = StatusCode.BadParam;
                    response.JsonBody       = new Body(new Dictionary <string, object> {
                        { "error", "invalid remote_url" }
                    });
                }
                else if (!FacebookAuthorizer.RegisterAccessToken(accessToken, email, siteUrl))
                {
                    response.InternalStatus = StatusCode.BadParam;
                    response.JsonBody       = new Body(new Dictionary <string, object> {
                        { "error", "invalid access_token" }
                    });
                }
                else
                {
                    response.JsonBody = new Body(new Dictionary <string, object> {
                        { "ok", "registered" },
                        { "email", email }
                    });
                }
            }
            else
            {
                response.InternalStatus = StatusCode.BadParam;
                response.JsonBody       = new Body(new Dictionary <string, object> {
                    { "error", "required fields: access_token, email, remote_url" }
                });
            }

            return(response.AsDefaultState());
        }
        /// <summary>
        /// Verifies and registers a persona token for use in replication authentication
        /// </summary>
        /// <returns>The response state for further HTTP processing</returns>
        /// <param name="context">The context of the Couchbase Lite HTTP request</param>
        public static ICouchbaseResponseState RegisterPersonaToken(ICouchbaseListenerContext context)
        {
            var response = context.CreateResponse();
            var body = context.BodyAs<Dictionary<string, object>>();

            string email = PersonaAuthorizer.RegisterAssertion(body.GetCast<string>("assertion"));
            if (email != null) {
                response.JsonBody = new Body(new Dictionary<string, object> {
                    { "ok", "registered" },
                    { "email", email }
                });
            } else {
                response.InternalStatus = StatusCode.BadParam;
                response.JsonBody = new Body(new Dictionary<string, object> {
                    { "error", "invalid assertion" }
                });
            }

            return response.AsDefaultState();
        }
Beispiel #6
0
        /// <summary>
        /// Verifies and registers a persona token for use in replication authentication
        /// </summary>
        /// <returns>The response state for further HTTP processing</returns>
        /// <param name="context">The context of the Couchbase Lite HTTP request</param>
        public static ICouchbaseResponseState RegisterPersonaToken(ICouchbaseListenerContext context)
        {
            var response = context.CreateResponse();
            var body     = context.BodyAs <Dictionary <string, object> >();

            string email = PersonaAuthorizer.RegisterAssertion(body.GetCast <string>("assertion"));

            if (email != null)
            {
                response.JsonBody = new Body(new Dictionary <string, object> {
                    { "ok", "registered" },
                    { "email", email }
                });
            }
            else
            {
                response.InternalStatus = StatusCode.BadParam;
                response.JsonBody       = new Body(new Dictionary <string, object> {
                    { "error", "invalid assertion" }
                });
            }

            return(response.AsDefaultState());
        }
        public static ICouchbaseResponseState RevsDiff(ICouchbaseListenerContext context)
        {
            // Collect all of the input doc/revision IDs as CBL_Revisions:
            var revs = new RevisionList();
            var body = context.BodyAs<Dictionary<string, object>>();
            if (body == null) {
                return context.CreateResponse(StatusCode.BadJson).AsDefaultState();
            }

            foreach (var docPair in body) {
                var revIDs = docPair.Value.AsList<string>();
                if (revIDs == null) {
                    return context.CreateResponse(StatusCode.BadParam).AsDefaultState();
                }

                foreach (var revID in revIDs) {
                    var rev = new RevisionInternal(docPair.Key, revID.AsRevID(), false);
                    revs.Add(rev);
                }
            }

            return PerformLogicWithDatabase(context, true, db =>
            {
                var response = context.CreateResponse();
                // Look them up, removing the existing ones from revs:
                db.Storage.FindMissingRevisions(revs);

                // Return the missing revs in a somewhat different format:
                IDictionary<string, object> diffs = new Dictionary<string, object>();
                foreach(var rev in revs) {
                    var docId = rev.DocID;
                    IList<RevisionID> missingRevs = null;
                    if(!diffs.ContainsKey(docId)) {
                        missingRevs = new List<RevisionID>();
                        diffs[docId] = new Dictionary<string, IList<RevisionID>> { { "missing", missingRevs } };
                    } else {
                        missingRevs = ((Dictionary<string, IList<RevisionID>>)diffs[docId])["missing"];
                    }

                    missingRevs.Add(rev.RevID);
                }

                // Add the possible ancestors for each missing revision:
                foreach(var docPair in diffs) {
                    IDictionary<string, IList<RevisionID>> docInfo = (IDictionary<string, IList<RevisionID>>)docPair.Value;
                    int maxGen = 0;
                    RevisionID maxRevID = null;
                    foreach(var revId in docInfo["missing"]) {
                        if(revId.Generation > maxGen) {
                            maxGen = revId.Generation;
                            maxRevID = revId;
                        }
                    }

                    var rev = new RevisionInternal(docPair.Key, maxRevID, false);
                    var ancestors = db.Storage.GetPossibleAncestors(rev, 0, ValueTypePtr<bool>.NULL)?.ToList();
                    if(ancestors != null && ancestors.Count > 0) {
                        docInfo["possible_ancestors"] = ancestors;
                    }
                }

                response.JsonBody = new Body(diffs);
                return response;
            }).AsDefaultState();

        }
        /// <summary>
        /// A database purge permanently removes the references to deleted documents from the database.
        /// </summary>
        /// <returns>The response state for further HTTP processing</returns>
        /// <param name="context">The context of the Couchbase Lite HTTP request</param>
        /// <remarks>
        /// http://docs.couchdb.org/en/latest/api/database/misc.html#post--db-_purge
        /// </remarks>
        public static ICouchbaseResponseState Purge(ICouchbaseListenerContext context)
        {
            return PerformLogicWithDatabase(context, true, db =>
            {
                var body = context.BodyAs<Dictionary<string, IList<string>>>();
                if(body == null) {
                    return context.CreateResponse(StatusCode.BadJson);
                }

                var purgedRevisions = db.Storage.PurgeRevisions(body);
                if(purgedRevisions == null) {
                    return context.CreateResponse(StatusCode.DbError);
                }

                var responseBody = new Body(new Dictionary<string, object>
                {
                    { "purged", purgedRevisions }
                });

                var retVal = context.CreateResponse();
                retVal.JsonBody = responseBody;
                return retVal;
            }).AsDefaultState();
        }
        public static ICouchbaseResponseState GetChangesPost(ICouchbaseListenerContext context)
        {
            DBMonitorCouchbaseResponseState responseState = new DBMonitorCouchbaseResponseState();

            var responseObject = PerformLogicWithDatabase(context, true, db =>
            {
                var response = context.CreateResponse();
                responseState.Response = response;
                var body = context.BodyAs<Dictionary<string, object>>();
                ProcessBody(body);
                if (body.GetCast<ChangesFeedMode>("feed") < ChangesFeedMode.Continuous) {
                    if(context.CacheWithEtag(db.GetLastSequenceNumber().ToString())) {
                        response.InternalStatus = StatusCode.NotModified;
                        return response;
                    }
                }

                var options = ChangesOptions.Default;
                responseState.Db = db;
                responseState.ContentOptions = body.GetCast<DocumentContentOptions>("content_options");
                responseState.ChangesFeedMode = body.GetCast<ChangesFeedMode>("feed");
                responseState.ChangesIncludeDocs = body.GetCast<bool>("include_docs");
                options.IncludeDocs = responseState.ChangesIncludeDocs;
                responseState.ChangesIncludeConflicts = body.GetCast<string>("style") == "all_docs";
                options.IncludeConflicts = responseState.ChangesIncludeConflicts;
                options.ContentOptions = responseState.ContentOptions;
                options.SortBySequence = !options.IncludeConflicts;
                options.Limit = body.GetCast<int>("limit", options.Limit);
                int since = body.GetCast<int>("since");

                string filterName = body.GetCast<string>("filter");
                if(filterName != null) {
                    Status status = new Status();
                    responseState.ChangesFilter = db.GetFilter(filterName, status);
                    if(responseState.ChangesFilter == null) {
                        return context.CreateResponse(status.Code);
                    }

                    responseState.FilterParams = context.GetQueryParams();
                }


                RevisionList changes = db.ChangesSince(since, options, responseState.ChangesFilter, responseState.FilterParams);
                if((responseState.ChangesFeedMode >= ChangesFeedMode.Continuous) || 
                    (responseState.ChangesFeedMode == ChangesFeedMode.LongPoll && changes.Count == 0)) {
                    // Response is going to stay open (continuous, or hanging GET):
                    response.Chunked = true;
                    if(responseState.ChangesFeedMode == ChangesFeedMode.EventSource) {
                        response["Content-Type"] = "text/event-stream; charset=utf-8";
                    }

                    if(responseState.ChangesFeedMode >= ChangesFeedMode.Continuous) {
                        response.WriteHeaders();
                        foreach(var rev in changes) {
                            response.SendContinuousLine(ChangesDictForRev(rev, responseState), context.ChangesFeedMode);
                        }
                    }

                    responseState.SubscribeToDatabase(db);
                    int heartbeat = body.GetCast<int>("heartbeat", Int32.MinValue);
                    if(heartbeat != Int32.MinValue) {
                        if(heartbeat <= 0) {
                            responseState.IsAsync = false;
                            return context.CreateResponse(StatusCode.BadParam);
                        }

                        heartbeat = Math.Max(heartbeat, (int)MinHeartbeat.TotalMilliseconds);
                        string heartbeatResponse = context.ChangesFeedMode == ChangesFeedMode.EventSource ? "\n\n" : "\r\n";
                        responseState.StartHeartbeat(heartbeatResponse, TimeSpan.FromMilliseconds(heartbeat));
                    }

                    return context.CreateResponse();
                } else {
                    if(responseState.ChangesIncludeConflicts) {
                        response.JsonBody = new Body(ResponseBodyForChanges(changes, since, options.Limit, responseState));
                    } else {
                        response.JsonBody = new Body(ResponseBodyForChanges(changes, since, responseState));
                    }

                    return response;
                }
            });

            responseState.Response = responseObject;
            return responseState;
        }
        /// <summary>
        /// Create and update multiple documents at the same time within a single request.
        /// </summary>
        /// <returns>The response state for further HTTP processing</returns>
        /// <param name="context">The context of the Couchbase Lite HTTP request</param>
        /// <remarks>
        /// http://docs.couchdb.org/en/latest/api/database/bulk-api.html#post--db-_bulk_docs
        /// </remarks>
        public static ICouchbaseResponseState ProcessDocumentChangeOperations(ICouchbaseListenerContext context)
        {
            return PerformLogicWithDatabase(context, true, db =>
            {
                var postBody = context.BodyAs<Dictionary<string, object>>();
                if(postBody == null) {
                    return context.CreateResponse(StatusCode.BadJson);
                }
                    
                if(!postBody.ContainsKey("docs")) {
                    return context.CreateResponse(StatusCode.BadParam);
                }
                var docs = postBody["docs"].AsList<IDictionary<string, object>>();

                bool allOrNothing;
                postBody.TryGetValue<bool>("all_or_nothing", out allOrNothing);

                bool newEdits;
                postBody.TryGetValue<bool>("new_edits", out newEdits);

                var response = context.CreateResponse();
                StatusCode status = StatusCode.Ok;
                bool success = db.RunInTransaction(() => {
                    List<IDictionary<string, object>> results = new List<IDictionary<string, object>>(docs.Count);
                    var castContext = context as ICouchbaseListenerContext2;
                    var source = castContext != null && !castContext.IsLoopbackRequest ? castContext.Sender : null;
                    foreach(var doc in docs) {
                        string docId = doc.CblID();
                        RevisionInternal rev = null;
                        Body body = new Body(doc);

                        if(!newEdits) {
                            if(!RevisionInternal.IsValid(body)) {
                                status = StatusCode.BadParam;
                            } else {
                                rev = new RevisionInternal(body);
                                var history = Database.ParseCouchDBRevisionHistory(doc);
                                try {
                                    db.ForceInsert(rev, history, source);
                                } catch(CouchbaseLiteException e) {
                                    status = e.Code;
                                }
                            } 
                        } else {
                            status = DocumentMethods.UpdateDocument(context, db, docId, body, false, allOrNothing, out rev);
                        }

                        IDictionary<string, object> result = null;
                        if((int)status < 300) {
                            Debug.Assert(rev != null && rev.RevID != null);
                            if(newEdits) {
                                result = new Dictionary<string, object>
                                {
                                    { "id", rev.DocID },
                                    { "rev", rev.RevID },
                                    { "status", (int)status }
                                };
                            }
                        } else if((int)status >= 500) {
                            return false; // abort the whole thing if something goes badly wrong
                        } else if(allOrNothing) {
                            return false; // all_or_nothing backs out if there's any error
                        } else {
                            var info = Status.ToHttpStatus(status);
                            result = new Dictionary<string, object>
                            {
                                { "id", docId },
                                { "error", info.Item2 },
                                { "status", info.Item1 }
                            };
                        }

                        if(result != null) {
                            results.Add(result);
                        }
                    }

                    response.JsonBody = new Body(results.Cast<object>().ToList());
                    return true;
                });

                if(!success) {
                    response.InternalStatus = status;
                }

                return response;
            }).AsDefaultState();
        }
        /// <summary>
        /// The POST to _all_docs allows to specify multiple keys to be selected from the database. 
        /// This enables you to request multiple documents in a single request, in place of multiple GET /{db}/{docid} requests.
        /// </summary>
        /// <returns>The response state for further HTTP processing</returns>
        /// <param name="context">The context of the Couchbase Lite HTTP request</param>
        /// <remarks>
        /// http://docs.couchdb.org/en/latest/api/database/bulk-api.html#post--db-_all_docs
        /// </remarks>
        public static ICouchbaseResponseState GetAllSpecifiedDocuments(ICouchbaseListenerContext context)
        {
            return PerformLogicWithDatabase(context, true, db =>
            {
                var options = context.QueryOptions;
                if(options == null) {
                    return context.CreateResponse(StatusCode.BadParam);
                }
                    
                var body = context.BodyAs<Dictionary<string, object>>();
                if(body == null) {
                    return context.CreateResponse(StatusCode.BadJson);
                }

                if(!body.ContainsKey("keys")) {
                    return context.CreateResponse(StatusCode.BadParam);
                }

                var keys = body["keys"].AsList<object>();
                options.Keys = keys;
                return DoAllDocs(context, db, options);
            }).AsDefaultState();
        }
Beispiel #12
0
        public static ICouchbaseResponseState RevsDiff(ICouchbaseListenerContext context)
        {
            // Collect all of the input doc/revision IDs as CBL_Revisions:
            var revs = new RevisionList();
            var body = context.BodyAs <Dictionary <string, object> >();

            if (body == null)
            {
                return(context.CreateResponse(StatusCode.BadJson).AsDefaultState());
            }

            foreach (var docPair in body)
            {
                var revIDs = docPair.Value.AsList <string>();
                if (revIDs == null)
                {
                    return(context.CreateResponse(StatusCode.BadParam).AsDefaultState());
                }

                foreach (var revID in revIDs)
                {
                    var rev = new RevisionInternal(docPair.Key, revID, false);
                    revs.Add(rev);
                }
            }

            return(PerformLogicWithDatabase(context, true, db =>
            {
                var response = context.CreateResponse();
                // Look them up, removing the existing ones from revs:
                db.Storage.FindMissingRevisions(revs);

                // Return the missing revs in a somewhat different format:
                IDictionary <string, object> diffs = new Dictionary <string, object>();
                foreach (var rev in revs)
                {
                    var docId = rev.GetDocId();
                    IList <string> missingRevs = null;
                    if (!diffs.ContainsKey(docId))
                    {
                        missingRevs = new List <string>();
                        diffs[docId] = new Dictionary <string, IList <string> > {
                            { "missing", missingRevs }
                        };
                    }
                    else
                    {
                        missingRevs = ((Dictionary <string, IList <string> >)diffs[docId])["missing"];
                    }

                    missingRevs.Add(rev.GetRevId());
                }

                // Add the possible ancestors for each missing revision:
                foreach (var docPair in diffs)
                {
                    IDictionary <string, IList <string> > docInfo = (IDictionary <string, IList <string> >)docPair.Value;
                    int maxGen = 0;
                    string maxRevID = null;
                    foreach (var revId in docInfo["missing"])
                    {
                        var parsed = RevisionInternal.ParseRevId(revId);
                        if (parsed.Item1 > maxGen)
                        {
                            maxGen = parsed.Item1;
                            maxRevID = revId;
                        }
                    }

                    var rev = new RevisionInternal(docPair.Key, maxRevID, false);
                    var ancestors = db.Storage.GetPossibleAncestors(rev, 0, false);
                    var ancestorList = ancestors == null ? null : ancestors.ToList();
                    if (ancestorList != null && ancestorList.Count > 0)
                    {
                        docInfo["possible_ancestors"] = ancestorList;
                    }
                }

                response.JsonBody = new Body(diffs);
                return response;
            }).AsDefaultState());
        }
Beispiel #13
0
        /// <summary>
        /// Create and update multiple documents at the same time within a single request.
        /// </summary>
        /// <returns>The response state for further HTTP processing</returns>
        /// <param name="context">The context of the Couchbase Lite HTTP request</param>
        /// <remarks>
        /// http://docs.couchdb.org/en/latest/api/database/bulk-api.html#post--db-_bulk_docs
        /// <remarks>
        public static ICouchbaseResponseState ProcessDocumentChangeOperations(ICouchbaseListenerContext context)
        {
            return(PerformLogicWithDatabase(context, true, db =>
            {
                var postBody = context.BodyAs <Dictionary <string, object> >();
                if (postBody == null)
                {
                    return context.CreateResponse(StatusCode.BadJson);
                }

                if (!postBody.ContainsKey("docs"))
                {
                    return context.CreateResponse(StatusCode.BadParam);
                }
                var docs = postBody["docs"].AsList <IDictionary <string, object> >();

                bool allOrNothing;
                postBody.TryGetValue <bool>("all_or_nothing", out allOrNothing);

                bool newEdits;
                postBody.TryGetValue <bool>("new_edits", out newEdits);

                var response = context.CreateResponse();
                StatusCode status = StatusCode.Ok;
                bool success = db.RunInTransaction(() => {
                    List <IDictionary <string, object> > results = new List <IDictionary <string, object> >(docs.Count);
                    foreach (var doc in docs)
                    {
                        string docId = doc.GetCast <string>("_id");
                        RevisionInternal rev = null;
                        Body body = new Body(doc);

                        if (!newEdits)
                        {
                            if (!RevisionInternal.IsValid(body))
                            {
                                status = StatusCode.BadParam;
                            }
                            else
                            {
                                rev = new RevisionInternal(body);
                                var history = Database.ParseCouchDBRevisionHistory(doc);
                                try {
                                    db.ForceInsert(rev, history, null);
                                } catch (CouchbaseLiteException e) {
                                    status = e.Code;
                                }
                            }
                        }
                        else
                        {
                            status = DocumentMethods.UpdateDocument(context, db, docId, body, false, allOrNothing, out rev);
                        }

                        IDictionary <string, object> result = null;
                        if ((int)status < 300)
                        {
                            Debug.Assert(rev != null && rev.GetRevId() != null);
                            if (newEdits)
                            {
                                result = new Dictionary <string, object>
                                {
                                    { "id", rev.GetDocId() },
                                    { "rev", rev.GetRevId() },
                                    { "status", (int)status }
                                };
                            }
                        }
                        else if ((int)status >= 500)
                        {
                            return false; // abort the whole thing if something goes badly wrong
                        }
                        else if (allOrNothing)
                        {
                            return false; // all_or_nothing backs out if there's any error
                        }
                        else
                        {
                            var info = Status.ToHttpStatus(status);
                            result = new Dictionary <string, object>
                            {
                                { "id", docId },
                                { "error", info.Item2 },
                                { "status", info.Item1 }
                            };
                        }

                        if (result != null)
                        {
                            results.Add(result);
                        }
                    }

                    response.JsonBody = new Body(results.Cast <object>().ToList());
                    return true;
                });

                if (!success)
                {
                    response.InternalStatus = status;
                }

                return response;
            }).AsDefaultState());
        }
Beispiel #14
0
        public static ICouchbaseResponseState GetChangesPost(ICouchbaseListenerContext context)
        {
            DBMonitorCouchbaseResponseState responseState = new DBMonitorCouchbaseResponseState();

            var responseObject = PerformLogicWithDatabase(context, true, db =>
            {
                var response           = context.CreateResponse();
                responseState.Response = response;
                var body = context.BodyAs <Dictionary <string, object> >();
                ProcessBody(body);
                if (body.GetCast <ChangesFeedMode>("feed") < ChangesFeedMode.Continuous)
                {
                    if (context.CacheWithEtag(db.GetLastSequenceNumber().ToString()))
                    {
                        response.InternalStatus = StatusCode.NotModified;
                        return(response);
                    }
                }

                var options                           = ChangesOptions.Default;
                responseState.Db                      = db;
                responseState.ContentOptions          = body.GetCast <DocumentContentOptions>("content_options");
                responseState.ChangesFeedMode         = body.GetCast <ChangesFeedMode>("feed");
                responseState.ChangesIncludeDocs      = body.GetCast <bool>("include_docs");
                options.IncludeDocs                   = responseState.ChangesIncludeDocs;
                responseState.ChangesIncludeConflicts = body.GetCast <string>("style") == "all_docs";
                options.IncludeConflicts              = responseState.ChangesIncludeConflicts;
                options.ContentOptions                = responseState.ContentOptions;
                options.SortBySequence                = !options.IncludeConflicts;
                options.Limit                         = body.GetCast <int>("limit", options.Limit);
                int since = body.GetCast <int>("since");

                string filterName = body.GetCast <string>("filter");
                if (filterName != null)
                {
                    Status status = new Status();
                    responseState.ChangesFilter = db.GetFilter(filterName, status);
                    if (responseState.ChangesFilter == null)
                    {
                        return(context.CreateResponse(status.Code));
                    }

                    responseState.FilterParams = context.GetQueryParams();
                }


                if (responseState.ChangesFeedMode >= ChangesFeedMode.LongPoll)
                {
                    // Response is going to stay open (continuous, or hanging GET):
                    response.Chunked = true;
                    if (responseState.ChangesFeedMode == ChangesFeedMode.EventSource)
                    {
                        response["Content-Type"] = "text/event-stream; charset=utf-8";
                    }

                    response.WriteHeaders();
                    if (responseState.SubscribeToDatabase(db, since, options))
                    {
                        int heartbeat = body.GetCast <int>("heartbeat", Int32.MinValue);
                        if (heartbeat != Int32.MinValue)
                        {
                            if (heartbeat <= 0)
                            {
                                responseState.IsAsync = false;
                                return(context.CreateResponse(StatusCode.BadParam));
                            }

                            heartbeat = Math.Max(heartbeat, (int)MinHeartbeat.TotalMilliseconds);
                            string heartbeatResponse = context.ChangesFeedMode == ChangesFeedMode.EventSource ? "\n\n" : "\r\n";
                            responseState.StartHeartbeat(heartbeatResponse, TimeSpan.FromMilliseconds(heartbeat));
                        }
                    }

                    return(responseState.Response);
                }
                else
                {
                    var changes      = db.ChangesSinceStreaming(since, options, responseState.ChangesFilter, responseState.FilterParams);
                    response.Chunked = true;
                    response.Headers["Content-Type"] = "application/json";
                    response.WriteBodyCallback       = WriteChangesBodyJson;
                    if (responseState.ChangesIncludeConflicts)
                    {
                        response.WriteBodyContext = new WriteChangesContext {
                            IncludeConflicts = true,
                            Since            = since,
                            Limit            = options.Limit,
                            Changes          = changes,
                            ResponseState    = responseState
                        };
                    }
                    else
                    {
                        response.WriteBodyContext = new WriteChangesContext {
                            Since         = since,
                            Changes       = changes,
                            ResponseState = responseState
                        };
                    }

                    return(response);
                }
            });

            responseState.Response = responseObject;
            return(responseState);
        }
        public static ICouchbaseResponseState GetChangesPost(ICouchbaseListenerContext context)
        {
            DBMonitorCouchbaseResponseState responseState = new DBMonitorCouchbaseResponseState();

            var responseObject = PerformLogicWithDatabase(context, true, db =>
            {
                var response           = context.CreateResponse();
                responseState.Response = response;
                var body = context.BodyAs <Dictionary <string, object> >();
                ProcessBody(body);
                if (body.GetCast <ChangesFeedMode>("feed") < ChangesFeedMode.Continuous)
                {
                    if (context.CacheWithEtag(db.GetLastSequenceNumber().ToString()))
                    {
                        response.InternalStatus = StatusCode.NotModified;
                        return(response);
                    }
                }

                var options                           = ChangesOptions.Default;
                responseState.Db                      = db;
                responseState.ContentOptions          = body.GetCast <DocumentContentOptions>("content_options");
                responseState.ChangesFeedMode         = body.GetCast <ChangesFeedMode>("feed");
                responseState.ChangesIncludeDocs      = body.GetCast <bool>("include_docs");
                options.IncludeDocs                   = responseState.ChangesIncludeDocs;
                responseState.ChangesIncludeConflicts = body.GetCast <string>("style") == "all_docs";
                options.IncludeConflicts              = responseState.ChangesIncludeConflicts;
                options.ContentOptions                = responseState.ContentOptions;
                options.SortBySequence                = !options.IncludeConflicts;
                options.Limit                         = body.GetCast <int>("limit", options.Limit);
                int since = body.GetCast <int>("since");

                string filterName = body.GetCast <string>("filter");
                if (filterName != null)
                {
                    Status status = new Status();
                    responseState.ChangesFilter = db.GetFilter(filterName, status);
                    if (responseState.ChangesFilter == null)
                    {
                        return(context.CreateResponse(status.Code));
                    }

                    responseState.FilterParams = context.GetQueryParams();
                }


                RevisionList changes = db.ChangesSince(since, options, responseState.ChangesFilter, responseState.FilterParams);
                if ((responseState.ChangesFeedMode >= ChangesFeedMode.Continuous) ||
                    (responseState.ChangesFeedMode == ChangesFeedMode.LongPoll && changes.Count == 0))
                {
                    // Response is going to stay open (continuous, or hanging GET):
                    response.Chunked = true;
                    if (responseState.ChangesFeedMode == ChangesFeedMode.EventSource)
                    {
                        response["Content-Type"] = "text/event-stream; charset=utf-8";
                    }

                    if (responseState.ChangesFeedMode >= ChangesFeedMode.Continuous)
                    {
                        response.WriteHeaders();
                        foreach (var rev in changes)
                        {
                            response.SendContinuousLine(ChangesDictForRev(rev, responseState), context.ChangesFeedMode);
                        }
                    }

                    responseState.SubscribeToDatabase(db);
                    int heartbeat = body.GetCast <int>("heartbeat", Int32.MinValue);
                    if (heartbeat != Int32.MinValue)
                    {
                        if (heartbeat <= 0)
                        {
                            responseState.IsAsync = false;
                            return(context.CreateResponse(StatusCode.BadParam));
                        }

                        heartbeat = Math.Min(heartbeat, MIN_HEARTBEAT);
                        string heartbeatResponse = context.ChangesFeedMode == ChangesFeedMode.EventSource ? "\n\n" : "\r\n";
                        responseState.StartHeartbeat(heartbeatResponse, heartbeat);
                    }

                    return(context.CreateResponse());
                }
                else
                {
                    if (responseState.ChangesIncludeConflicts)
                    {
                        response.JsonBody = new Body(ResponseBodyForChanges(changes, since, options.Limit, responseState));
                    }
                    else
                    {
                        response.JsonBody = new Body(ResponseBodyForChanges(changes, since, responseState));
                    }

                    return(response);
                }
            });

            responseState.Response = responseObject;
            return(responseState);
        }