{"action":"created","issue":{"url":"https://api.github.com/repos/NancyFx/Nancy/issues/2089","labels_url":"https://api.github.com/repos/NancyFx/Nancy/issues/2089/labels{/name}","comments_url":"https://api.github.com/repos/NancyFx/Nancy/issues/2089/comments","events_url":"https://api.github.com/repos/NancyFx/Nancy/issues/2089/events","html_url":"https://github.com/NancyFx/Nancy/pull/2089","id":113607539,"number":2089,"title":"In proc sessions","user":{"login":"DavidLievrouw","id":13859270,"avatar_url":"https://avatars.githubusercontent.com/u/13859270?v=3","gravatar_id":"","url":"https://api.github.com/users/DavidLievrouw","html_url":"https://github.com/DavidLievrouw","followers_url":"https://api.github.com/users/DavidLievrouw/followers","following_url":"https://api.github.com/users/DavidLievrouw/following{/other_user}","gists_url":"https://api.github.com/users/DavidLievrouw/gists{/gist_id}","starred_url":"https://api.github.com/users/DavidLievrouw/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DavidLievrouw/subscriptions","organizations_url":"https://api.github.com/users/DavidLievrouw/orgs","repos_url":"https://api.github.com/users/DavidLievrouw/repos","events_url":"https://api.github.com/users/DavidLievrouw/events{/privacy}","received_events_url":"https://api.github.com/users/DavidLievrouw/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"milestone":null,"comments":11,"created_at":"2015-10-27T15:12:05Z","updated_at":"2015-10-29T15:32:14Z","closed_at":"2015-10-28T13:21:27Z","pull_request":{"url":"https://api.github.com/repos/NancyFx/Nancy/pulls/2089","html_url":"https://github.com/NancyFx/Nancy/pull/2089","diff_url":"https://github.com/NancyFx/Nancy/pull/2089.diff","patch_url":"https://github.com/NancyFx/Nancy/pull/2089.patch"},"body":"# In-process sessions\r\n\r\nThis feature was developed in order to be able to work with in-process memory sessions. The current NancyFX version only works with cookie-based sessions, that contain all the session data. All the session data is sent over-the-wire with every request.\r\n\r\nA couple of reasons for doing this:\r\n- In my case, I keep a non-serializable object in the session. This is not serializable to a string to put in the cookie. So I would like to keep it in memory, and only send a lightweight correlation ID around.\r\n- When working with larger objects, the cookie data gets quite large. And that's going over the wire with every request. \r\n- I would like to be able to use cookieless sessions, over HTTPS.\r\n\r\n### Super-Duper-Happy-Path\r\n\r\n```cs\r\n    public class Bootstrapper : DefaultNancyBootstrapper\r\n    {\r\n        protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)\r\n        {\r\n            base.ApplicationStartup(container, pipelines);\r\n            InProcSessions.Enable(pipelines);\r\n        }\r\n    }\r\n```\r\nThis command enables the in-process sessions, using the default configuration options:\r\n- A cookie is created, containing **only** the unique identifier of the session.\r\n- This cookie is encrypted using CryptographyConfiguration.Default.\r\n- The cookie name is \"_nsid\".\r\n- There is no Domain or Path setting for the cookie.\r\n- The session timeout is 20 minutes.\r\n- Expired sessions are cleaned automatically every 30 minutes.\r\n\r\n### Configuration options\r\n```cs\r\n    public class Bootstrapper : DefaultNancyBootstrapper\r\n    {\r\n        protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)\r\n        {\r\n            base.ApplicationStartup(container, pipelines);\r\n            \r\n            var sessionConfig = new InProcSessionsConfiguration\r\n            {\r\n                SessionTimeout = TimeSpan.FromMinutes(3),\r\n                CacheTrimInterval = TimeSpan.FromMinutes(10),\r\n                SessionIdentificationMethod = new BySessionIdCookieIdentificationMethod(CryptographyConfiguration.NoEncryption)\r\n                {\r\n                    CookieName = \"Mycookie\",\r\n                    Domain = \".nascar.com\",\r\n                    Path = \"/schedule/\"\r\n                }\r\n            };\r\n            InProcSessions.Enable(pipelines, sessionConfig);\r\n        }\r\n    }\r\n```\r\n\r\nYou can specify several configuration options when enabling this feature:\r\n- SessionTimeout: The timeout after which sessions expire.\r\n- CacheTrimInterval: Time interval between automatic cleanup actions of expired sessions (async).\r\n- SessionIdentificationMethod:\r\n  - Defines the method by which the session is identified. Two implementations are provided:\r\n    - BySessionIdCookie (default):\r\n      - CookieName, CookiePath and CookieDomain.\r\n      - CryptographyConfiguration.\r\n    - ByQueryStringParam (for cookieless sessions, more on that later)\r\n      - ParameterName: The name of the querystring parameter in which the session identifier is stored. \r\n      - CryptographyConfiguration.\r\n\r\n### Cookieless sessions\r\nThe default session identification method is BySessionIdCookie. This is very similar with how the current CookieBasedSessions feature works: It creates a cookie that is sent with each request, in order to be able to load the session.\r\nThe big difference: The session data is not in the cookie value itself. Only a correlation ID.\r\n\r\nWhen I worked with ASP.NET, there was a feature called cookieless sessions. There are a lot of pros and cons to consider, when implementing this. But I found it useful in the past. So I thought it would be a nice feature to add to NancyFX.\r\n\r\n```cs\r\npublic class Bootstrapper : DefaultNancyBootstrapper\r\n    {\r\n        protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)\r\n        {\r\n            var sessionConfig = new InProcSessionsConfiguration\r\n            {\r\n                SessionIdentificationMethod = new ByQueryStringParamIdentificationMethod(CryptographyConfiguration.Default)\r\n            };\r\n            InProcSessions.Enable(pipelines, sessionConfig);\r\n\r\n            base.ApplicationStartup(container, pipelines);\r\n        }\r\n    }\r\n```\r\n\r\nWhen navigating to a page without the session ID querystring parameter, a 302-FOUND response with a location header is sent back to the client. This location header redirects to the same location, but includes the session ID querystring parameter.\r\n\r\n### Sample\r\nI added a tiny sample project to the solution: Nancy.Demo.InProcSessions.\r\n\r\n### Roll your own\r\nCurrently, two session identification methods are implemented:\r\n- BySessionIdCookie\r\n- ByQueryStringParam\r\n\r\nYou can create your own method. Implement the interface and wire it up.\r\n\r\n```cs\r\n    /// <summary>\r\n    /// Identification method for in-process memory based sessions.\r\n    /// </summary>\r\n    public class MySessionIdentificationMethod : IInProcSessionIdentificationMethod\r\n    {\r\n        /// <summary>\r\n        /// Load the session identifier from the specified context.\r\n        /// </summary>\r\n        /// <param name=\"context\">The current context.</param>\r\n        /// <returns>The identifier of the session for the current request.</returns>\r\n        public SessionId GetCurrentSessionId(NancyContext context)\r\n        {\r\n            // Load the session identifier from the context.\r\n            // Or create a new one, if no session data is sent with the request.\r\n        }\r\n\r\n        /// <summary>\r\n        /// Save the session identifier in the specified context.\r\n        /// </summary>\r\n        /// <param name=\"sessionId\">The identifier of the session.</param>\r\n        /// <param name=\"context\">The current context.</param>\r\n        public void SaveSessionId(SessionId sessionId, NancyContext context)\r\n        {\r\n            // Make sure that the next request from the client\r\n            // contains this session ID.\r\n        }\r\n    }\r\n```\r\n\r\nAnd modify your bootstrapper:\r\n```cs\r\n    public class Bootstrapper : DefaultNancyBootstrapper\r\n    {\r\n        protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)\r\n        {\r\n            var sessionConfig = new InProcSessionsConfiguration\r\n            {\r\n                SessionIdentificationMethod = new MySessionIdentificationMethod()\r\n            };\r\n            InProcSessions.Enable(pipelines, sessionConfig);\r\n\r\n            base.ApplicationStartup(container, pipelines);\r\n        }\r\n    }\r\n```\r\n\r\n### Breaking changes\r\nNone, it's completely new, in a separate namespace.\r\n\r\n### Not happy about...\r\nI needed to update the SharedAssemblyInfo.cs file, in order to be able to fake internal interfaces using FakeItEasy. I added this line:\r\n\r\n```cs\r\n[assembly: InternalsVisibleTo(\"DynamicProxyGenAssembly2\")]\r\n```\r\n\r\nNot really happy about that. But I didn't want to make all the feature internals publicly visible. Users should not be bothered with a bunch of internal interfaces, in my opinion.\r\n\r\n### Not interested?\r\nWhen this feature doesn't get pulled, I would like to provide it as a NuGet package.\r\n\r\n### Contact\r\nFeel free to contact me. I would like some feedback on this.\r\ndavid.lievrouw at gmail.com"},"comment":{"url":"https://api.github.com/repos/NancyFx/Nancy/issues/comments/152216167","html_url":"https://github.com/NancyFx/Nancy/pull/2089#issuecomment-152216167","issue_url":"https://api.github.com/repos/NancyFx/Nancy/issues/2089","id":152216167,"user":{"login":"phillip-haydon","id":895629,"avatar_url":"https://avatars.githubusercontent.com/u/895629?v=3","gravatar_id":"","url":"https://api.github.com/users/phillip-haydon","html_url":"https://github.com/phillip-haydon","followers_url":"https://api.github.com/users/phillip-haydon/followers","following_url":"https://api.github.com/users/phillip-haydon/following{/other_user}","gists_url":"https://api.github.com/users/phillip-haydon/gists{/gist_id}","starred_url":"https://api.github.com/users/phillip-haydon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/phillip-haydon/subscriptions","organizations_url":"https://api.github.com/users/phillip-haydon/orgs","repos_url":"https://api.github.com/users/phillip-haydon/repos","events_url":"https://api.github.com/users/phillip-haydon/events{/privacy}","received_events_url":"https://api.github.com/users/phillip-haydon/received_events","type":"User","site_admin":false},"created_at":"2015-10-29T15:32:14Z","updated_at":"2015-10-29T15:32:14Z","body":"Awesome stuff! <3"}}